editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788                priority: 0,
 3789            }],
 3790            Some(Autoscroll::fit()),
 3791            cx,
 3792        );
 3793        editor.change_selections(None, cx, |s| {
 3794            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3795        });
 3796        editor.move_line_down(&MoveLineDown, cx);
 3797    });
 3798}
 3799
 3800#[gpui::test]
 3801fn test_transpose(cx: &mut TestAppContext) {
 3802    init_test(cx, |_| {});
 3803
 3804    _ = cx.add_window(|cx| {
 3805        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3806        editor.set_style(EditorStyle::default(), cx);
 3807        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3808        editor.transpose(&Default::default(), cx);
 3809        assert_eq!(editor.text(cx), "bac");
 3810        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3811
 3812        editor.transpose(&Default::default(), cx);
 3813        assert_eq!(editor.text(cx), "bca");
 3814        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3815
 3816        editor.transpose(&Default::default(), cx);
 3817        assert_eq!(editor.text(cx), "bac");
 3818        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3819
 3820        editor
 3821    });
 3822
 3823    _ = cx.add_window(|cx| {
 3824        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3825        editor.set_style(EditorStyle::default(), cx);
 3826        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3827        editor.transpose(&Default::default(), cx);
 3828        assert_eq!(editor.text(cx), "acb\nde");
 3829        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3830
 3831        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3832        editor.transpose(&Default::default(), cx);
 3833        assert_eq!(editor.text(cx), "acbd\ne");
 3834        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3835
 3836        editor.transpose(&Default::default(), cx);
 3837        assert_eq!(editor.text(cx), "acbde\n");
 3838        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3839
 3840        editor.transpose(&Default::default(), cx);
 3841        assert_eq!(editor.text(cx), "acbd\ne");
 3842        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3843
 3844        editor
 3845    });
 3846
 3847    _ = cx.add_window(|cx| {
 3848        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3849        editor.set_style(EditorStyle::default(), cx);
 3850        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3851        editor.transpose(&Default::default(), cx);
 3852        assert_eq!(editor.text(cx), "bacd\ne");
 3853        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3854
 3855        editor.transpose(&Default::default(), cx);
 3856        assert_eq!(editor.text(cx), "bcade\n");
 3857        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3858
 3859        editor.transpose(&Default::default(), cx);
 3860        assert_eq!(editor.text(cx), "bcda\ne");
 3861        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3862
 3863        editor.transpose(&Default::default(), cx);
 3864        assert_eq!(editor.text(cx), "bcade\n");
 3865        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3866
 3867        editor.transpose(&Default::default(), cx);
 3868        assert_eq!(editor.text(cx), "bcaed\n");
 3869        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3870
 3871        editor
 3872    });
 3873
 3874    _ = cx.add_window(|cx| {
 3875        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3876        editor.set_style(EditorStyle::default(), cx);
 3877        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3878        editor.transpose(&Default::default(), cx);
 3879        assert_eq!(editor.text(cx), "🏀🍐✋");
 3880        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3881
 3882        editor.transpose(&Default::default(), cx);
 3883        assert_eq!(editor.text(cx), "🏀✋🍐");
 3884        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3885
 3886        editor.transpose(&Default::default(), cx);
 3887        assert_eq!(editor.text(cx), "🏀🍐✋");
 3888        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3889
 3890        editor
 3891    });
 3892}
 3893
 3894#[gpui::test]
 3895async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3896    init_test(cx, |_| {});
 3897
 3898    let mut cx = EditorTestContext::new(cx).await;
 3899
 3900    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3901    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3902    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3903
 3904    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3905    cx.set_state("two ˇfour ˇsix ˇ");
 3906    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3907    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3908
 3909    // Paste again but with only two cursors. Since the number of cursors doesn't
 3910    // match the number of slices in the clipboard, the entire clipboard text
 3911    // is pasted at each cursor.
 3912    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3913    cx.update_editor(|e, cx| {
 3914        e.handle_input("( ", cx);
 3915        e.paste(&Paste, cx);
 3916        e.handle_input(") ", cx);
 3917    });
 3918    cx.assert_editor_state(
 3919        &([
 3920            "( one✅ ",
 3921            "three ",
 3922            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3923            "three ",
 3924            "five ) ˇ",
 3925        ]
 3926        .join("\n")),
 3927    );
 3928
 3929    // Cut with three selections, one of which is full-line.
 3930    cx.set_state(indoc! {"
 3931        1«2ˇ»3
 3932        4ˇ567
 3933        «8ˇ»9"});
 3934    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3935    cx.assert_editor_state(indoc! {"
 3936        1ˇ3
 3937        ˇ9"});
 3938
 3939    // Paste with three selections, noticing how the copied selection that was full-line
 3940    // gets inserted before the second cursor.
 3941    cx.set_state(indoc! {"
 3942        1ˇ3
 3943 3944        «oˇ»ne"});
 3945    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3946    cx.assert_editor_state(indoc! {"
 3947        12ˇ3
 3948        4567
 3949 3950        8ˇne"});
 3951
 3952    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3953    cx.set_state(indoc! {"
 3954        The quick brown
 3955        fox juˇmps over
 3956        the lazy dog"});
 3957    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3958    assert_eq!(
 3959        cx.read_from_clipboard()
 3960            .and_then(|item| item.text().as_deref().map(str::to_string)),
 3961        Some("fox jumps over\n".to_string())
 3962    );
 3963
 3964    // Paste with three selections, noticing how the copied full-line selection is inserted
 3965    // before the empty selections but replaces the selection that is non-empty.
 3966    cx.set_state(indoc! {"
 3967        Tˇhe quick brown
 3968        «foˇ»x jumps over
 3969        tˇhe lazy dog"});
 3970    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        fox jumps over
 3973        Tˇhe quick brown
 3974        fox jumps over
 3975        ˇx jumps over
 3976        fox jumps over
 3977        tˇhe lazy dog"});
 3978}
 3979
 3980#[gpui::test]
 3981async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3982    init_test(cx, |_| {});
 3983
 3984    let mut cx = EditorTestContext::new(cx).await;
 3985    let language = Arc::new(Language::new(
 3986        LanguageConfig::default(),
 3987        Some(tree_sitter_rust::language()),
 3988    ));
 3989    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3990
 3991    // Cut an indented block, without the leading whitespace.
 3992    cx.set_state(indoc! {"
 3993        const a: B = (
 3994            c(),
 3995            «d(
 3996                e,
 3997                f
 3998            )ˇ»
 3999        );
 4000    "});
 4001    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4002    cx.assert_editor_state(indoc! {"
 4003        const a: B = (
 4004            c(),
 4005            ˇ
 4006        );
 4007    "});
 4008
 4009    // Paste it at the same position.
 4010    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4011    cx.assert_editor_state(indoc! {"
 4012        const a: B = (
 4013            c(),
 4014            d(
 4015                e,
 4016                f
 4017 4018        );
 4019    "});
 4020
 4021    // Paste it at a line with a lower indent level.
 4022    cx.set_state(indoc! {"
 4023        ˇ
 4024        const a: B = (
 4025            c(),
 4026        );
 4027    "});
 4028    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4029    cx.assert_editor_state(indoc! {"
 4030        d(
 4031            e,
 4032            f
 4033 4034        const a: B = (
 4035            c(),
 4036        );
 4037    "});
 4038
 4039    // Cut an indented block, with the leading whitespace.
 4040    cx.set_state(indoc! {"
 4041        const a: B = (
 4042            c(),
 4043        «    d(
 4044                e,
 4045                f
 4046            )
 4047        ˇ»);
 4048    "});
 4049    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        const a: B = (
 4052            c(),
 4053        ˇ);
 4054    "});
 4055
 4056    // Paste it at the same position.
 4057    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4058    cx.assert_editor_state(indoc! {"
 4059        const a: B = (
 4060            c(),
 4061            d(
 4062                e,
 4063                f
 4064            )
 4065        ˇ);
 4066    "});
 4067
 4068    // Paste it at a line with a higher indent level.
 4069    cx.set_state(indoc! {"
 4070        const a: B = (
 4071            c(),
 4072            d(
 4073                e,
 4074 4075            )
 4076        );
 4077    "});
 4078    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4079    cx.assert_editor_state(indoc! {"
 4080        const a: B = (
 4081            c(),
 4082            d(
 4083                e,
 4084                f    d(
 4085                    e,
 4086                    f
 4087                )
 4088        ˇ
 4089            )
 4090        );
 4091    "});
 4092}
 4093
 4094#[gpui::test]
 4095fn test_select_all(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    let view = cx.add_window(|cx| {
 4099        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4100        build_editor(buffer, cx)
 4101    });
 4102    _ = view.update(cx, |view, cx| {
 4103        view.select_all(&SelectAll, cx);
 4104        assert_eq!(
 4105            view.selections.display_ranges(cx),
 4106            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4107        );
 4108    });
 4109}
 4110
 4111#[gpui::test]
 4112fn test_select_line(cx: &mut TestAppContext) {
 4113    init_test(cx, |_| {});
 4114
 4115    let view = cx.add_window(|cx| {
 4116        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4117        build_editor(buffer, cx)
 4118    });
 4119    _ = view.update(cx, |view, cx| {
 4120        view.change_selections(None, cx, |s| {
 4121            s.select_display_ranges([
 4122                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4123                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4125                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4126            ])
 4127        });
 4128        view.select_line(&SelectLine, cx);
 4129        assert_eq!(
 4130            view.selections.display_ranges(cx),
 4131            vec![
 4132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4133                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4134            ]
 4135        );
 4136    });
 4137
 4138    _ = view.update(cx, |view, cx| {
 4139        view.select_line(&SelectLine, cx);
 4140        assert_eq!(
 4141            view.selections.display_ranges(cx),
 4142            vec![
 4143                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4144                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4145            ]
 4146        );
 4147    });
 4148
 4149    _ = view.update(cx, |view, cx| {
 4150        view.select_line(&SelectLine, cx);
 4151        assert_eq!(
 4152            view.selections.display_ranges(cx),
 4153            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4154        );
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let view = cx.add_window(|cx| {
 4163        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4164        build_editor(buffer, cx)
 4165    });
 4166    _ = view.update(cx, |view, cx| {
 4167        view.fold_ranges(
 4168            vec![
 4169                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4170                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4171                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4172            ],
 4173            true,
 4174            cx,
 4175        );
 4176        view.change_selections(None, cx, |s| {
 4177            s.select_display_ranges([
 4178                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4179                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4180                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4181                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4182            ])
 4183        });
 4184        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4185    });
 4186
 4187    _ = view.update(cx, |view, cx| {
 4188        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4189        assert_eq!(
 4190            view.display_text(cx),
 4191            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4192        );
 4193        assert_eq!(
 4194            view.selections.display_ranges(cx),
 4195            [
 4196                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4197                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4198                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4199                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4200            ]
 4201        );
 4202    });
 4203
 4204    _ = view.update(cx, |view, cx| {
 4205        view.change_selections(None, cx, |s| {
 4206            s.select_display_ranges([
 4207                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4208            ])
 4209        });
 4210        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4211        assert_eq!(
 4212            view.display_text(cx),
 4213            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4214        );
 4215        assert_eq!(
 4216            view.selections.display_ranges(cx),
 4217            [
 4218                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4219                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4220                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4221                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4222                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4223                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4224                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4225                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4226            ]
 4227        );
 4228    });
 4229}
 4230
 4231#[gpui::test]
 4232async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    let mut cx = EditorTestContext::new(cx).await;
 4236
 4237    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4238    cx.set_state(indoc!(
 4239        r#"abc
 4240           defˇghi
 4241
 4242           jk
 4243           nlmo
 4244           "#
 4245    ));
 4246
 4247    cx.update_editor(|editor, cx| {
 4248        editor.add_selection_above(&Default::default(), cx);
 4249    });
 4250
 4251    cx.assert_editor_state(indoc!(
 4252        r#"abcˇ
 4253           defˇghi
 4254
 4255           jk
 4256           nlmo
 4257           "#
 4258    ));
 4259
 4260    cx.update_editor(|editor, cx| {
 4261        editor.add_selection_above(&Default::default(), cx);
 4262    });
 4263
 4264    cx.assert_editor_state(indoc!(
 4265        r#"abcˇ
 4266            defˇghi
 4267
 4268            jk
 4269            nlmo
 4270            "#
 4271    ));
 4272
 4273    cx.update_editor(|view, cx| {
 4274        view.add_selection_below(&Default::default(), cx);
 4275    });
 4276
 4277    cx.assert_editor_state(indoc!(
 4278        r#"abc
 4279           defˇghi
 4280
 4281           jk
 4282           nlmo
 4283           "#
 4284    ));
 4285
 4286    cx.update_editor(|view, cx| {
 4287        view.undo_selection(&Default::default(), cx);
 4288    });
 4289
 4290    cx.assert_editor_state(indoc!(
 4291        r#"abcˇ
 4292           defˇghi
 4293
 4294           jk
 4295           nlmo
 4296           "#
 4297    ));
 4298
 4299    cx.update_editor(|view, cx| {
 4300        view.redo_selection(&Default::default(), cx);
 4301    });
 4302
 4303    cx.assert_editor_state(indoc!(
 4304        r#"abc
 4305           defˇghi
 4306
 4307           jk
 4308           nlmo
 4309           "#
 4310    ));
 4311
 4312    cx.update_editor(|view, cx| {
 4313        view.add_selection_below(&Default::default(), cx);
 4314    });
 4315
 4316    cx.assert_editor_state(indoc!(
 4317        r#"abc
 4318           defˇghi
 4319
 4320           jk
 4321           nlmˇo
 4322           "#
 4323    ));
 4324
 4325    cx.update_editor(|view, cx| {
 4326        view.add_selection_below(&Default::default(), cx);
 4327    });
 4328
 4329    cx.assert_editor_state(indoc!(
 4330        r#"abc
 4331           defˇghi
 4332
 4333           jk
 4334           nlmˇo
 4335           "#
 4336    ));
 4337
 4338    // change selections
 4339    cx.set_state(indoc!(
 4340        r#"abc
 4341           def«ˇg»hi
 4342
 4343           jk
 4344           nlmo
 4345           "#
 4346    ));
 4347
 4348    cx.update_editor(|view, cx| {
 4349        view.add_selection_below(&Default::default(), cx);
 4350    });
 4351
 4352    cx.assert_editor_state(indoc!(
 4353        r#"abc
 4354           def«ˇg»hi
 4355
 4356           jk
 4357           nlm«ˇo»
 4358           "#
 4359    ));
 4360
 4361    cx.update_editor(|view, cx| {
 4362        view.add_selection_below(&Default::default(), cx);
 4363    });
 4364
 4365    cx.assert_editor_state(indoc!(
 4366        r#"abc
 4367           def«ˇg»hi
 4368
 4369           jk
 4370           nlm«ˇo»
 4371           "#
 4372    ));
 4373
 4374    cx.update_editor(|view, cx| {
 4375        view.add_selection_above(&Default::default(), cx);
 4376    });
 4377
 4378    cx.assert_editor_state(indoc!(
 4379        r#"abc
 4380           def«ˇg»hi
 4381
 4382           jk
 4383           nlmo
 4384           "#
 4385    ));
 4386
 4387    cx.update_editor(|view, cx| {
 4388        view.add_selection_above(&Default::default(), cx);
 4389    });
 4390
 4391    cx.assert_editor_state(indoc!(
 4392        r#"abc
 4393           def«ˇg»hi
 4394
 4395           jk
 4396           nlmo
 4397           "#
 4398    ));
 4399
 4400    // Change selections again
 4401    cx.set_state(indoc!(
 4402        r#"a«bc
 4403           defgˇ»hi
 4404
 4405           jk
 4406           nlmo
 4407           "#
 4408    ));
 4409
 4410    cx.update_editor(|view, cx| {
 4411        view.add_selection_below(&Default::default(), cx);
 4412    });
 4413
 4414    cx.assert_editor_state(indoc!(
 4415        r#"a«bcˇ»
 4416           d«efgˇ»hi
 4417
 4418           j«kˇ»
 4419           nlmo
 4420           "#
 4421    ));
 4422
 4423    cx.update_editor(|view, cx| {
 4424        view.add_selection_below(&Default::default(), cx);
 4425    });
 4426    cx.assert_editor_state(indoc!(
 4427        r#"a«bcˇ»
 4428           d«efgˇ»hi
 4429
 4430           j«kˇ»
 4431           n«lmoˇ»
 4432           "#
 4433    ));
 4434    cx.update_editor(|view, cx| {
 4435        view.add_selection_above(&Default::default(), cx);
 4436    });
 4437
 4438    cx.assert_editor_state(indoc!(
 4439        r#"a«bcˇ»
 4440           d«efgˇ»hi
 4441
 4442           j«kˇ»
 4443           nlmo
 4444           "#
 4445    ));
 4446
 4447    // Change selections again
 4448    cx.set_state(indoc!(
 4449        r#"abc
 4450           d«ˇefghi
 4451
 4452           jk
 4453           nlm»o
 4454           "#
 4455    ));
 4456
 4457    cx.update_editor(|view, cx| {
 4458        view.add_selection_above(&Default::default(), cx);
 4459    });
 4460
 4461    cx.assert_editor_state(indoc!(
 4462        r#"a«ˇbc»
 4463           d«ˇef»ghi
 4464
 4465           j«ˇk»
 4466           n«ˇlm»o
 4467           "#
 4468    ));
 4469
 4470    cx.update_editor(|view, cx| {
 4471        view.add_selection_below(&Default::default(), cx);
 4472    });
 4473
 4474    cx.assert_editor_state(indoc!(
 4475        r#"abc
 4476           d«ˇef»ghi
 4477
 4478           j«ˇk»
 4479           n«ˇlm»o
 4480           "#
 4481    ));
 4482}
 4483
 4484#[gpui::test]
 4485async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4486    init_test(cx, |_| {});
 4487
 4488    let mut cx = EditorTestContext::new(cx).await;
 4489    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4490
 4491    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4492        .unwrap();
 4493    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4494
 4495    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4496        .unwrap();
 4497    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4498
 4499    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4500    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4501
 4502    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4503    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4504
 4505    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4506        .unwrap();
 4507    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4508
 4509    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4510        .unwrap();
 4511    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4512}
 4513
 4514#[gpui::test]
 4515async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4516    init_test(cx, |_| {});
 4517
 4518    let mut cx = EditorTestContext::new(cx).await;
 4519    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4520
 4521    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4522        .unwrap();
 4523    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4524}
 4525
 4526#[gpui::test]
 4527async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4528    init_test(cx, |_| {});
 4529
 4530    let mut cx = EditorTestContext::new(cx).await;
 4531    cx.set_state(
 4532        r#"let foo = 2;
 4533lˇet foo = 2;
 4534let fooˇ = 2;
 4535let foo = 2;
 4536let foo = ˇ2;"#,
 4537    );
 4538
 4539    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4540        .unwrap();
 4541    cx.assert_editor_state(
 4542        r#"let foo = 2;
 4543«letˇ» foo = 2;
 4544let «fooˇ» = 2;
 4545let foo = 2;
 4546let foo = «2ˇ»;"#,
 4547    );
 4548
 4549    // noop for multiple selections with different contents
 4550    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4551        .unwrap();
 4552    cx.assert_editor_state(
 4553        r#"let foo = 2;
 4554«letˇ» foo = 2;
 4555let «fooˇ» = 2;
 4556let foo = 2;
 4557let foo = «2ˇ»;"#,
 4558    );
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new_multibuffer(
 4566        cx,
 4567        [
 4568            &indoc! {
 4569                "aaa\n«bbb\nccc\n»ddd"
 4570            },
 4571            &indoc! {
 4572                "aaa\n«bbb\nccc\n»ddd"
 4573            },
 4574        ],
 4575    );
 4576
 4577    cx.assert_editor_state(indoc! {"
 4578        ˇbbb
 4579        ccc
 4580
 4581        bbb
 4582        ccc
 4583        "});
 4584    cx.dispatch_action(SelectPrevious::default());
 4585    cx.assert_editor_state(indoc! {"
 4586                «bbbˇ»
 4587                ccc
 4588
 4589                bbb
 4590                ccc
 4591                "});
 4592    cx.dispatch_action(SelectPrevious::default());
 4593    cx.assert_editor_state(indoc! {"
 4594                «bbbˇ»
 4595                ccc
 4596
 4597                «bbbˇ»
 4598                ccc
 4599                "});
 4600}
 4601
 4602#[gpui::test]
 4603async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4604    init_test(cx, |_| {});
 4605
 4606    let mut cx = EditorTestContext::new(cx).await;
 4607    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4608
 4609    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4610        .unwrap();
 4611    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4612
 4613    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4614        .unwrap();
 4615    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4616
 4617    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4618    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4619
 4620    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4621    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4622
 4623    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4624        .unwrap();
 4625    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4626
 4627    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4628        .unwrap();
 4629    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4630
 4631    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4632        .unwrap();
 4633    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4634}
 4635
 4636#[gpui::test]
 4637async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4638    init_test(cx, |_| {});
 4639
 4640    let mut cx = EditorTestContext::new(cx).await;
 4641    cx.set_state(
 4642        r#"let foo = 2;
 4643lˇet foo = 2;
 4644let fooˇ = 2;
 4645let foo = 2;
 4646let foo = ˇ2;"#,
 4647    );
 4648
 4649    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4650        .unwrap();
 4651    cx.assert_editor_state(
 4652        r#"let foo = 2;
 4653«letˇ» foo = 2;
 4654let «fooˇ» = 2;
 4655let foo = 2;
 4656let foo = «2ˇ»;"#,
 4657    );
 4658
 4659    // noop for multiple selections with different contents
 4660    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4661        .unwrap();
 4662    cx.assert_editor_state(
 4663        r#"let foo = 2;
 4664«letˇ» foo = 2;
 4665let «fooˇ» = 2;
 4666let foo = 2;
 4667let foo = «2ˇ»;"#,
 4668    );
 4669}
 4670
 4671#[gpui::test]
 4672async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4673    init_test(cx, |_| {});
 4674
 4675    let mut cx = EditorTestContext::new(cx).await;
 4676    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4677
 4678    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4679        .unwrap();
 4680    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4681
 4682    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4683        .unwrap();
 4684    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4685
 4686    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4687    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4688
 4689    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4690    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4691
 4692    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4693        .unwrap();
 4694    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4695
 4696    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4697        .unwrap();
 4698    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4699}
 4700
 4701#[gpui::test]
 4702async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4703    init_test(cx, |_| {});
 4704
 4705    let language = Arc::new(Language::new(
 4706        LanguageConfig::default(),
 4707        Some(tree_sitter_rust::language()),
 4708    ));
 4709
 4710    let text = r#"
 4711        use mod1::mod2::{mod3, mod4};
 4712
 4713        fn fn_1(param1: bool, param2: &str) {
 4714            let var1 = "text";
 4715        }
 4716    "#
 4717    .unindent();
 4718
 4719    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4720    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4721    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4722
 4723    editor
 4724        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4725        .await;
 4726
 4727    editor.update(cx, |view, cx| {
 4728        view.change_selections(None, cx, |s| {
 4729            s.select_display_ranges([
 4730                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4731                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4732                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4733            ]);
 4734        });
 4735        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4736    });
 4737    editor.update(cx, |editor, cx| {
 4738        assert_text_with_selections(
 4739            editor,
 4740            indoc! {r#"
 4741                use mod1::mod2::{mod3, «mod4ˇ»};
 4742
 4743                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4744                    let var1 = "«textˇ»";
 4745                }
 4746            "#},
 4747            cx,
 4748        );
 4749    });
 4750
 4751    editor.update(cx, |view, cx| {
 4752        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4753    });
 4754    editor.update(cx, |editor, cx| {
 4755        assert_text_with_selections(
 4756            editor,
 4757            indoc! {r#"
 4758                use mod1::mod2::«{mod3, mod4}ˇ»;
 4759
 4760                «ˇfn fn_1(param1: bool, param2: &str) {
 4761                    let var1 = "text";
 4762 4763            "#},
 4764            cx,
 4765        );
 4766    });
 4767
 4768    editor.update(cx, |view, cx| {
 4769        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4770    });
 4771    assert_eq!(
 4772        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4773        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4774    );
 4775
 4776    // Trying to expand the selected syntax node one more time has no effect.
 4777    editor.update(cx, |view, cx| {
 4778        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4779    });
 4780    assert_eq!(
 4781        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4782        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4783    );
 4784
 4785    editor.update(cx, |view, cx| {
 4786        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4787    });
 4788    editor.update(cx, |editor, cx| {
 4789        assert_text_with_selections(
 4790            editor,
 4791            indoc! {r#"
 4792                use mod1::mod2::«{mod3, mod4}ˇ»;
 4793
 4794                «ˇfn fn_1(param1: bool, param2: &str) {
 4795                    let var1 = "text";
 4796 4797            "#},
 4798            cx,
 4799        );
 4800    });
 4801
 4802    editor.update(cx, |view, cx| {
 4803        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4804    });
 4805    editor.update(cx, |editor, cx| {
 4806        assert_text_with_selections(
 4807            editor,
 4808            indoc! {r#"
 4809                use mod1::mod2::{mod3, «mod4ˇ»};
 4810
 4811                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4812                    let var1 = "«textˇ»";
 4813                }
 4814            "#},
 4815            cx,
 4816        );
 4817    });
 4818
 4819    editor.update(cx, |view, cx| {
 4820        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4821    });
 4822    editor.update(cx, |editor, cx| {
 4823        assert_text_with_selections(
 4824            editor,
 4825            indoc! {r#"
 4826                use mod1::mod2::{mod3, mo«ˇ»d4};
 4827
 4828                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4829                    let var1 = "te«ˇ»xt";
 4830                }
 4831            "#},
 4832            cx,
 4833        );
 4834    });
 4835
 4836    // Trying to shrink the selected syntax node one more time has no effect.
 4837    editor.update(cx, |view, cx| {
 4838        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4839    });
 4840    editor.update(cx, |editor, cx| {
 4841        assert_text_with_selections(
 4842            editor,
 4843            indoc! {r#"
 4844                use mod1::mod2::{mod3, mo«ˇ»d4};
 4845
 4846                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4847                    let var1 = "te«ˇ»xt";
 4848                }
 4849            "#},
 4850            cx,
 4851        );
 4852    });
 4853
 4854    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4855    // a fold.
 4856    editor.update(cx, |view, cx| {
 4857        view.fold_ranges(
 4858            vec![
 4859                (
 4860                    Point::new(0, 21)..Point::new(0, 24),
 4861                    FoldPlaceholder::test(),
 4862                ),
 4863                (
 4864                    Point::new(3, 20)..Point::new(3, 22),
 4865                    FoldPlaceholder::test(),
 4866                ),
 4867            ],
 4868            true,
 4869            cx,
 4870        );
 4871        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4872    });
 4873    editor.update(cx, |editor, cx| {
 4874        assert_text_with_selections(
 4875            editor,
 4876            indoc! {r#"
 4877                use mod1::mod2::«{mod3, mod4}ˇ»;
 4878
 4879                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4880                    «let var1 = "text";ˇ»
 4881                }
 4882            "#},
 4883            cx,
 4884        );
 4885    });
 4886}
 4887
 4888#[gpui::test]
 4889async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4890    init_test(cx, |_| {});
 4891
 4892    let language = Arc::new(
 4893        Language::new(
 4894            LanguageConfig {
 4895                brackets: BracketPairConfig {
 4896                    pairs: vec![
 4897                        BracketPair {
 4898                            start: "{".to_string(),
 4899                            end: "}".to_string(),
 4900                            close: false,
 4901                            surround: false,
 4902                            newline: true,
 4903                        },
 4904                        BracketPair {
 4905                            start: "(".to_string(),
 4906                            end: ")".to_string(),
 4907                            close: false,
 4908                            surround: false,
 4909                            newline: true,
 4910                        },
 4911                    ],
 4912                    ..Default::default()
 4913                },
 4914                ..Default::default()
 4915            },
 4916            Some(tree_sitter_rust::language()),
 4917        )
 4918        .with_indents_query(
 4919            r#"
 4920                (_ "(" ")" @end) @indent
 4921                (_ "{" "}" @end) @indent
 4922            "#,
 4923        )
 4924        .unwrap(),
 4925    );
 4926
 4927    let text = "fn a() {}";
 4928
 4929    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4930    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4931    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4932    editor
 4933        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4934        .await;
 4935
 4936    _ = editor.update(cx, |editor, cx| {
 4937        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4938        editor.newline(&Newline, cx);
 4939        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4940        assert_eq!(
 4941            editor.selections.ranges(cx),
 4942            &[
 4943                Point::new(1, 4)..Point::new(1, 4),
 4944                Point::new(3, 4)..Point::new(3, 4),
 4945                Point::new(5, 0)..Point::new(5, 0)
 4946            ]
 4947        );
 4948    });
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let mut cx = EditorTestContext::new(cx).await;
 4956
 4957    let language = Arc::new(Language::new(
 4958        LanguageConfig {
 4959            brackets: BracketPairConfig {
 4960                pairs: vec![
 4961                    BracketPair {
 4962                        start: "{".to_string(),
 4963                        end: "}".to_string(),
 4964                        close: true,
 4965                        surround: true,
 4966                        newline: true,
 4967                    },
 4968                    BracketPair {
 4969                        start: "(".to_string(),
 4970                        end: ")".to_string(),
 4971                        close: true,
 4972                        surround: true,
 4973                        newline: true,
 4974                    },
 4975                    BracketPair {
 4976                        start: "/*".to_string(),
 4977                        end: " */".to_string(),
 4978                        close: true,
 4979                        surround: true,
 4980                        newline: true,
 4981                    },
 4982                    BracketPair {
 4983                        start: "[".to_string(),
 4984                        end: "]".to_string(),
 4985                        close: false,
 4986                        surround: false,
 4987                        newline: true,
 4988                    },
 4989                    BracketPair {
 4990                        start: "\"".to_string(),
 4991                        end: "\"".to_string(),
 4992                        close: true,
 4993                        surround: true,
 4994                        newline: false,
 4995                    },
 4996                    BracketPair {
 4997                        start: "<".to_string(),
 4998                        end: ">".to_string(),
 4999                        close: false,
 5000                        surround: true,
 5001                        newline: true,
 5002                    },
 5003                ],
 5004                ..Default::default()
 5005            },
 5006            autoclose_before: "})]".to_string(),
 5007            ..Default::default()
 5008        },
 5009        Some(tree_sitter_rust::language()),
 5010    ));
 5011
 5012    cx.language_registry().add(language.clone());
 5013    cx.update_buffer(|buffer, cx| {
 5014        buffer.set_language(Some(language), cx);
 5015    });
 5016
 5017    cx.set_state(
 5018        &r#"
 5019            🏀ˇ
 5020            εˇ
 5021            ❤️ˇ
 5022        "#
 5023        .unindent(),
 5024    );
 5025
 5026    // autoclose multiple nested brackets at multiple cursors
 5027    cx.update_editor(|view, cx| {
 5028        view.handle_input("{", cx);
 5029        view.handle_input("{", cx);
 5030        view.handle_input("{", cx);
 5031    });
 5032    cx.assert_editor_state(
 5033        &"
 5034            🏀{{{ˇ}}}
 5035            ε{{{ˇ}}}
 5036            ❤️{{{ˇ}}}
 5037        "
 5038        .unindent(),
 5039    );
 5040
 5041    // insert a different closing bracket
 5042    cx.update_editor(|view, cx| {
 5043        view.handle_input(")", cx);
 5044    });
 5045    cx.assert_editor_state(
 5046        &"
 5047            🏀{{{)ˇ}}}
 5048            ε{{{)ˇ}}}
 5049            ❤️{{{)ˇ}}}
 5050        "
 5051        .unindent(),
 5052    );
 5053
 5054    // skip over the auto-closed brackets when typing a closing bracket
 5055    cx.update_editor(|view, cx| {
 5056        view.move_right(&MoveRight, cx);
 5057        view.handle_input("}", cx);
 5058        view.handle_input("}", cx);
 5059        view.handle_input("}", cx);
 5060    });
 5061    cx.assert_editor_state(
 5062        &"
 5063            🏀{{{)}}}}ˇ
 5064            ε{{{)}}}}ˇ
 5065            ❤️{{{)}}}}ˇ
 5066        "
 5067        .unindent(),
 5068    );
 5069
 5070    // autoclose multi-character pairs
 5071    cx.set_state(
 5072        &"
 5073            ˇ
 5074            ˇ
 5075        "
 5076        .unindent(),
 5077    );
 5078    cx.update_editor(|view, cx| {
 5079        view.handle_input("/", cx);
 5080        view.handle_input("*", cx);
 5081    });
 5082    cx.assert_editor_state(
 5083        &"
 5084            /*ˇ */
 5085            /*ˇ */
 5086        "
 5087        .unindent(),
 5088    );
 5089
 5090    // one cursor autocloses a multi-character pair, one cursor
 5091    // does not autoclose.
 5092    cx.set_state(
 5093        &"
 5094 5095            ˇ
 5096        "
 5097        .unindent(),
 5098    );
 5099    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5100    cx.assert_editor_state(
 5101        &"
 5102            /*ˇ */
 5103 5104        "
 5105        .unindent(),
 5106    );
 5107
 5108    // Don't autoclose if the next character isn't whitespace and isn't
 5109    // listed in the language's "autoclose_before" section.
 5110    cx.set_state("ˇa b");
 5111    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5112    cx.assert_editor_state("{ˇa b");
 5113
 5114    // Don't autoclose if `close` is false for the bracket pair
 5115    cx.set_state("ˇ");
 5116    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5117    cx.assert_editor_state("");
 5118
 5119    // Surround with brackets if text is selected
 5120    cx.set_state("«aˇ» b");
 5121    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5122    cx.assert_editor_state("{«aˇ»} b");
 5123
 5124    // Autclose pair where the start and end characters are the same
 5125    cx.set_state("");
 5126    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5127    cx.assert_editor_state("a\"ˇ\"");
 5128    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5129    cx.assert_editor_state("a\"\"ˇ");
 5130
 5131    // Don't autoclose pair if autoclose is disabled
 5132    cx.set_state("ˇ");
 5133    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5134    cx.assert_editor_state("");
 5135
 5136    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5137    cx.set_state("«aˇ» b");
 5138    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5139    cx.assert_editor_state("<«aˇ»> b");
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |settings| {
 5145        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5146    });
 5147
 5148    let mut cx = EditorTestContext::new(cx).await;
 5149
 5150    let language = Arc::new(Language::new(
 5151        LanguageConfig {
 5152            brackets: BracketPairConfig {
 5153                pairs: vec![
 5154                    BracketPair {
 5155                        start: "{".to_string(),
 5156                        end: "}".to_string(),
 5157                        close: true,
 5158                        surround: true,
 5159                        newline: true,
 5160                    },
 5161                    BracketPair {
 5162                        start: "(".to_string(),
 5163                        end: ")".to_string(),
 5164                        close: true,
 5165                        surround: true,
 5166                        newline: true,
 5167                    },
 5168                    BracketPair {
 5169                        start: "[".to_string(),
 5170                        end: "]".to_string(),
 5171                        close: false,
 5172                        surround: false,
 5173                        newline: true,
 5174                    },
 5175                ],
 5176                ..Default::default()
 5177            },
 5178            autoclose_before: "})]".to_string(),
 5179            ..Default::default()
 5180        },
 5181        Some(tree_sitter_rust::language()),
 5182    ));
 5183
 5184    cx.language_registry().add(language.clone());
 5185    cx.update_buffer(|buffer, cx| {
 5186        buffer.set_language(Some(language), cx);
 5187    });
 5188
 5189    cx.set_state(
 5190        &"
 5191            ˇ
 5192            ˇ
 5193            ˇ
 5194        "
 5195        .unindent(),
 5196    );
 5197
 5198    // ensure only matching closing brackets are skipped over
 5199    cx.update_editor(|view, cx| {
 5200        view.handle_input("}", cx);
 5201        view.move_left(&MoveLeft, cx);
 5202        view.handle_input(")", cx);
 5203        view.move_left(&MoveLeft, cx);
 5204    });
 5205    cx.assert_editor_state(
 5206        &"
 5207            ˇ)}
 5208            ˇ)}
 5209            ˇ)}
 5210        "
 5211        .unindent(),
 5212    );
 5213
 5214    // skip-over closing brackets at multiple cursors
 5215    cx.update_editor(|view, cx| {
 5216        view.handle_input(")", cx);
 5217        view.handle_input("}", cx);
 5218    });
 5219    cx.assert_editor_state(
 5220        &"
 5221            )}ˇ
 5222            )}ˇ
 5223            )}ˇ
 5224        "
 5225        .unindent(),
 5226    );
 5227
 5228    // ignore non-close brackets
 5229    cx.update_editor(|view, cx| {
 5230        view.handle_input("]", cx);
 5231        view.move_left(&MoveLeft, cx);
 5232        view.handle_input("]", cx);
 5233    });
 5234    cx.assert_editor_state(
 5235        &"
 5236            )}]ˇ]
 5237            )}]ˇ]
 5238            )}]ˇ]
 5239        "
 5240        .unindent(),
 5241    );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249
 5250    let html_language = Arc::new(
 5251        Language::new(
 5252            LanguageConfig {
 5253                name: "HTML".into(),
 5254                brackets: BracketPairConfig {
 5255                    pairs: vec![
 5256                        BracketPair {
 5257                            start: "<".into(),
 5258                            end: ">".into(),
 5259                            close: true,
 5260                            ..Default::default()
 5261                        },
 5262                        BracketPair {
 5263                            start: "{".into(),
 5264                            end: "}".into(),
 5265                            close: true,
 5266                            ..Default::default()
 5267                        },
 5268                        BracketPair {
 5269                            start: "(".into(),
 5270                            end: ")".into(),
 5271                            close: true,
 5272                            ..Default::default()
 5273                        },
 5274                    ],
 5275                    ..Default::default()
 5276                },
 5277                autoclose_before: "})]>".into(),
 5278                ..Default::default()
 5279            },
 5280            Some(tree_sitter_html::language()),
 5281        )
 5282        .with_injection_query(
 5283            r#"
 5284            (script_element
 5285                (raw_text) @content
 5286                (#set! "language" "javascript"))
 5287            "#,
 5288        )
 5289        .unwrap(),
 5290    );
 5291
 5292    let javascript_language = Arc::new(Language::new(
 5293        LanguageConfig {
 5294            name: "JavaScript".into(),
 5295            brackets: BracketPairConfig {
 5296                pairs: vec![
 5297                    BracketPair {
 5298                        start: "/*".into(),
 5299                        end: " */".into(),
 5300                        close: true,
 5301                        ..Default::default()
 5302                    },
 5303                    BracketPair {
 5304                        start: "{".into(),
 5305                        end: "}".into(),
 5306                        close: true,
 5307                        ..Default::default()
 5308                    },
 5309                    BracketPair {
 5310                        start: "(".into(),
 5311                        end: ")".into(),
 5312                        close: true,
 5313                        ..Default::default()
 5314                    },
 5315                ],
 5316                ..Default::default()
 5317            },
 5318            autoclose_before: "})]>".into(),
 5319            ..Default::default()
 5320        },
 5321        Some(tree_sitter_typescript::language_tsx()),
 5322    ));
 5323
 5324    cx.language_registry().add(html_language.clone());
 5325    cx.language_registry().add(javascript_language.clone());
 5326
 5327    cx.update_buffer(|buffer, cx| {
 5328        buffer.set_language(Some(html_language), cx);
 5329    });
 5330
 5331    cx.set_state(
 5332        &r#"
 5333            <body>ˇ
 5334                <script>
 5335                    var x = 1;ˇ
 5336                </script>
 5337            </body>ˇ
 5338        "#
 5339        .unindent(),
 5340    );
 5341
 5342    // Precondition: different languages are active at different locations.
 5343    cx.update_editor(|editor, cx| {
 5344        let snapshot = editor.snapshot(cx);
 5345        let cursors = editor.selections.ranges::<usize>(cx);
 5346        let languages = cursors
 5347            .iter()
 5348            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5349            .collect::<Vec<_>>();
 5350        assert_eq!(
 5351            languages,
 5352            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5353        );
 5354    });
 5355
 5356    // Angle brackets autoclose in HTML, but not JavaScript.
 5357    cx.update_editor(|editor, cx| {
 5358        editor.handle_input("<", cx);
 5359        editor.handle_input("a", cx);
 5360    });
 5361    cx.assert_editor_state(
 5362        &r#"
 5363            <body><aˇ>
 5364                <script>
 5365                    var x = 1;<aˇ
 5366                </script>
 5367            </body><aˇ>
 5368        "#
 5369        .unindent(),
 5370    );
 5371
 5372    // Curly braces and parens autoclose in both HTML and JavaScript.
 5373    cx.update_editor(|editor, cx| {
 5374        editor.handle_input(" b=", cx);
 5375        editor.handle_input("{", cx);
 5376        editor.handle_input("c", cx);
 5377        editor.handle_input("(", cx);
 5378    });
 5379    cx.assert_editor_state(
 5380        &r#"
 5381            <body><a b={c(ˇ)}>
 5382                <script>
 5383                    var x = 1;<a b={c(ˇ)}
 5384                </script>
 5385            </body><a b={c(ˇ)}>
 5386        "#
 5387        .unindent(),
 5388    );
 5389
 5390    // Brackets that were already autoclosed are skipped.
 5391    cx.update_editor(|editor, cx| {
 5392        editor.handle_input(")", cx);
 5393        editor.handle_input("d", cx);
 5394        editor.handle_input("}", cx);
 5395    });
 5396    cx.assert_editor_state(
 5397        &r#"
 5398            <body><a b={c()d}ˇ>
 5399                <script>
 5400                    var x = 1;<a b={c()d}ˇ
 5401                </script>
 5402            </body><a b={c()d}ˇ>
 5403        "#
 5404        .unindent(),
 5405    );
 5406    cx.update_editor(|editor, cx| {
 5407        editor.handle_input(">", cx);
 5408    });
 5409    cx.assert_editor_state(
 5410        &r#"
 5411            <body><a b={c()d}>ˇ
 5412                <script>
 5413                    var x = 1;<a b={c()d}>ˇ
 5414                </script>
 5415            </body><a b={c()d}>ˇ
 5416        "#
 5417        .unindent(),
 5418    );
 5419
 5420    // Reset
 5421    cx.set_state(
 5422        &r#"
 5423            <body>ˇ
 5424                <script>
 5425                    var x = 1;ˇ
 5426                </script>
 5427            </body>ˇ
 5428        "#
 5429        .unindent(),
 5430    );
 5431
 5432    cx.update_editor(|editor, cx| {
 5433        editor.handle_input("<", cx);
 5434    });
 5435    cx.assert_editor_state(
 5436        &r#"
 5437            <body><ˇ>
 5438                <script>
 5439                    var x = 1;<ˇ
 5440                </script>
 5441            </body><ˇ>
 5442        "#
 5443        .unindent(),
 5444    );
 5445
 5446    // When backspacing, the closing angle brackets are removed.
 5447    cx.update_editor(|editor, cx| {
 5448        editor.backspace(&Backspace, cx);
 5449    });
 5450    cx.assert_editor_state(
 5451        &r#"
 5452            <body>ˇ
 5453                <script>
 5454                    var x = 1;ˇ
 5455                </script>
 5456            </body>ˇ
 5457        "#
 5458        .unindent(),
 5459    );
 5460
 5461    // Block comments autoclose in JavaScript, but not HTML.
 5462    cx.update_editor(|editor, cx| {
 5463        editor.handle_input("/", cx);
 5464        editor.handle_input("*", cx);
 5465    });
 5466    cx.assert_editor_state(
 5467        &r#"
 5468            <body>/*ˇ
 5469                <script>
 5470                    var x = 1;/*ˇ */
 5471                </script>
 5472            </body>/*ˇ
 5473        "#
 5474        .unindent(),
 5475    );
 5476}
 5477
 5478#[gpui::test]
 5479async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5480    init_test(cx, |_| {});
 5481
 5482    let mut cx = EditorTestContext::new(cx).await;
 5483
 5484    let rust_language = Arc::new(
 5485        Language::new(
 5486            LanguageConfig {
 5487                name: "Rust".into(),
 5488                brackets: serde_json::from_value(json!([
 5489                    { "start": "{", "end": "}", "close": true, "newline": true },
 5490                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5491                ]))
 5492                .unwrap(),
 5493                autoclose_before: "})]>".into(),
 5494                ..Default::default()
 5495            },
 5496            Some(tree_sitter_rust::language()),
 5497        )
 5498        .with_override_query("(string_literal) @string")
 5499        .unwrap(),
 5500    );
 5501
 5502    cx.language_registry().add(rust_language.clone());
 5503    cx.update_buffer(|buffer, cx| {
 5504        buffer.set_language(Some(rust_language), cx);
 5505    });
 5506
 5507    cx.set_state(
 5508        &r#"
 5509            let x = ˇ
 5510        "#
 5511        .unindent(),
 5512    );
 5513
 5514    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5515    cx.update_editor(|editor, cx| {
 5516        editor.handle_input("\"", cx);
 5517    });
 5518    cx.assert_editor_state(
 5519        &r#"
 5520            let x = "ˇ"
 5521        "#
 5522        .unindent(),
 5523    );
 5524
 5525    // Inserting another quotation mark. The cursor moves across the existing
 5526    // automatically-inserted quotation mark.
 5527    cx.update_editor(|editor, cx| {
 5528        editor.handle_input("\"", cx);
 5529    });
 5530    cx.assert_editor_state(
 5531        &r#"
 5532            let x = ""ˇ
 5533        "#
 5534        .unindent(),
 5535    );
 5536
 5537    // Reset
 5538    cx.set_state(
 5539        &r#"
 5540            let x = ˇ
 5541        "#
 5542        .unindent(),
 5543    );
 5544
 5545    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5546    cx.update_editor(|editor, cx| {
 5547        editor.handle_input("\"", cx);
 5548        editor.handle_input(" ", cx);
 5549        editor.move_left(&Default::default(), cx);
 5550        editor.handle_input("\\", cx);
 5551        editor.handle_input("\"", cx);
 5552    });
 5553    cx.assert_editor_state(
 5554        &r#"
 5555            let x = "\"ˇ "
 5556        "#
 5557        .unindent(),
 5558    );
 5559
 5560    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5561    // mark. Nothing is inserted.
 5562    cx.update_editor(|editor, cx| {
 5563        editor.move_right(&Default::default(), cx);
 5564        editor.handle_input("\"", cx);
 5565    });
 5566    cx.assert_editor_state(
 5567        &r#"
 5568            let x = "\" "ˇ
 5569        "#
 5570        .unindent(),
 5571    );
 5572}
 5573
 5574#[gpui::test]
 5575async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5576    init_test(cx, |_| {});
 5577
 5578    let language = Arc::new(Language::new(
 5579        LanguageConfig {
 5580            brackets: BracketPairConfig {
 5581                pairs: vec![
 5582                    BracketPair {
 5583                        start: "{".to_string(),
 5584                        end: "}".to_string(),
 5585                        close: true,
 5586                        surround: true,
 5587                        newline: true,
 5588                    },
 5589                    BracketPair {
 5590                        start: "/* ".to_string(),
 5591                        end: "*/".to_string(),
 5592                        close: true,
 5593                        surround: true,
 5594                        ..Default::default()
 5595                    },
 5596                ],
 5597                ..Default::default()
 5598            },
 5599            ..Default::default()
 5600        },
 5601        Some(tree_sitter_rust::language()),
 5602    ));
 5603
 5604    let text = r#"
 5605        a
 5606        b
 5607        c
 5608    "#
 5609    .unindent();
 5610
 5611    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5612    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5613    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5614    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5615        .await;
 5616
 5617    _ = view.update(cx, |view, cx| {
 5618        view.change_selections(None, cx, |s| {
 5619            s.select_display_ranges([
 5620                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5621                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5622                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5623            ])
 5624        });
 5625
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628        view.handle_input("{", cx);
 5629        assert_eq!(
 5630            view.text(cx),
 5631            "
 5632                {{{a}}}
 5633                {{{b}}}
 5634                {{{c}}}
 5635            "
 5636            .unindent()
 5637        );
 5638        assert_eq!(
 5639            view.selections.display_ranges(cx),
 5640            [
 5641                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5642                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5643                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5644            ]
 5645        );
 5646
 5647        view.undo(&Undo, cx);
 5648        view.undo(&Undo, cx);
 5649        view.undo(&Undo, cx);
 5650        assert_eq!(
 5651            view.text(cx),
 5652            "
 5653                a
 5654                b
 5655                c
 5656            "
 5657            .unindent()
 5658        );
 5659        assert_eq!(
 5660            view.selections.display_ranges(cx),
 5661            [
 5662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5663                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5664                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5665            ]
 5666        );
 5667
 5668        // Ensure inserting the first character of a multi-byte bracket pair
 5669        // doesn't surround the selections with the bracket.
 5670        view.handle_input("/", cx);
 5671        assert_eq!(
 5672            view.text(cx),
 5673            "
 5674                /
 5675                /
 5676                /
 5677            "
 5678            .unindent()
 5679        );
 5680        assert_eq!(
 5681            view.selections.display_ranges(cx),
 5682            [
 5683                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5686            ]
 5687        );
 5688
 5689        view.undo(&Undo, cx);
 5690        assert_eq!(
 5691            view.text(cx),
 5692            "
 5693                a
 5694                b
 5695                c
 5696            "
 5697            .unindent()
 5698        );
 5699        assert_eq!(
 5700            view.selections.display_ranges(cx),
 5701            [
 5702                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5703                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5704                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5705            ]
 5706        );
 5707
 5708        // Ensure inserting the last character of a multi-byte bracket pair
 5709        // doesn't surround the selections with the bracket.
 5710        view.handle_input("*", cx);
 5711        assert_eq!(
 5712            view.text(cx),
 5713            "
 5714                *
 5715                *
 5716                *
 5717            "
 5718            .unindent()
 5719        );
 5720        assert_eq!(
 5721            view.selections.display_ranges(cx),
 5722            [
 5723                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5724                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5725                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5726            ]
 5727        );
 5728    });
 5729}
 5730
 5731#[gpui::test]
 5732async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5733    init_test(cx, |_| {});
 5734
 5735    let language = Arc::new(Language::new(
 5736        LanguageConfig {
 5737            brackets: BracketPairConfig {
 5738                pairs: vec![BracketPair {
 5739                    start: "{".to_string(),
 5740                    end: "}".to_string(),
 5741                    close: true,
 5742                    surround: true,
 5743                    newline: true,
 5744                }],
 5745                ..Default::default()
 5746            },
 5747            autoclose_before: "}".to_string(),
 5748            ..Default::default()
 5749        },
 5750        Some(tree_sitter_rust::language()),
 5751    ));
 5752
 5753    let text = r#"
 5754        a
 5755        b
 5756        c
 5757    "#
 5758    .unindent();
 5759
 5760    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5761    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5762    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5763    editor
 5764        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5765        .await;
 5766
 5767    _ = editor.update(cx, |editor, cx| {
 5768        editor.change_selections(None, cx, |s| {
 5769            s.select_ranges([
 5770                Point::new(0, 1)..Point::new(0, 1),
 5771                Point::new(1, 1)..Point::new(1, 1),
 5772                Point::new(2, 1)..Point::new(2, 1),
 5773            ])
 5774        });
 5775
 5776        editor.handle_input("{", cx);
 5777        editor.handle_input("{", cx);
 5778        editor.handle_input("_", cx);
 5779        assert_eq!(
 5780            editor.text(cx),
 5781            "
 5782                a{{_}}
 5783                b{{_}}
 5784                c{{_}}
 5785            "
 5786            .unindent()
 5787        );
 5788        assert_eq!(
 5789            editor.selections.ranges::<Point>(cx),
 5790            [
 5791                Point::new(0, 4)..Point::new(0, 4),
 5792                Point::new(1, 4)..Point::new(1, 4),
 5793                Point::new(2, 4)..Point::new(2, 4)
 5794            ]
 5795        );
 5796
 5797        editor.backspace(&Default::default(), cx);
 5798        editor.backspace(&Default::default(), cx);
 5799        assert_eq!(
 5800            editor.text(cx),
 5801            "
 5802                a{}
 5803                b{}
 5804                c{}
 5805            "
 5806            .unindent()
 5807        );
 5808        assert_eq!(
 5809            editor.selections.ranges::<Point>(cx),
 5810            [
 5811                Point::new(0, 2)..Point::new(0, 2),
 5812                Point::new(1, 2)..Point::new(1, 2),
 5813                Point::new(2, 2)..Point::new(2, 2)
 5814            ]
 5815        );
 5816
 5817        editor.delete_to_previous_word_start(&Default::default(), cx);
 5818        assert_eq!(
 5819            editor.text(cx),
 5820            "
 5821                a
 5822                b
 5823                c
 5824            "
 5825            .unindent()
 5826        );
 5827        assert_eq!(
 5828            editor.selections.ranges::<Point>(cx),
 5829            [
 5830                Point::new(0, 1)..Point::new(0, 1),
 5831                Point::new(1, 1)..Point::new(1, 1),
 5832                Point::new(2, 1)..Point::new(2, 1)
 5833            ]
 5834        );
 5835    });
 5836}
 5837
 5838#[gpui::test]
 5839async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5840    init_test(cx, |settings| {
 5841        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5842    });
 5843
 5844    let mut cx = EditorTestContext::new(cx).await;
 5845
 5846    let language = Arc::new(Language::new(
 5847        LanguageConfig {
 5848            brackets: BracketPairConfig {
 5849                pairs: vec![
 5850                    BracketPair {
 5851                        start: "{".to_string(),
 5852                        end: "}".to_string(),
 5853                        close: true,
 5854                        surround: true,
 5855                        newline: true,
 5856                    },
 5857                    BracketPair {
 5858                        start: "(".to_string(),
 5859                        end: ")".to_string(),
 5860                        close: true,
 5861                        surround: true,
 5862                        newline: true,
 5863                    },
 5864                    BracketPair {
 5865                        start: "[".to_string(),
 5866                        end: "]".to_string(),
 5867                        close: false,
 5868                        surround: true,
 5869                        newline: true,
 5870                    },
 5871                ],
 5872                ..Default::default()
 5873            },
 5874            autoclose_before: "})]".to_string(),
 5875            ..Default::default()
 5876        },
 5877        Some(tree_sitter_rust::language()),
 5878    ));
 5879
 5880    cx.language_registry().add(language.clone());
 5881    cx.update_buffer(|buffer, cx| {
 5882        buffer.set_language(Some(language), cx);
 5883    });
 5884
 5885    cx.set_state(
 5886        &"
 5887            {(ˇ)}
 5888            [[ˇ]]
 5889            {(ˇ)}
 5890        "
 5891        .unindent(),
 5892    );
 5893
 5894    cx.update_editor(|view, cx| {
 5895        view.backspace(&Default::default(), cx);
 5896        view.backspace(&Default::default(), cx);
 5897    });
 5898
 5899    cx.assert_editor_state(
 5900        &"
 5901            ˇ
 5902            ˇ]]
 5903            ˇ
 5904        "
 5905        .unindent(),
 5906    );
 5907
 5908    cx.update_editor(|view, cx| {
 5909        view.handle_input("{", cx);
 5910        view.handle_input("{", cx);
 5911        view.move_right(&MoveRight, cx);
 5912        view.move_right(&MoveRight, cx);
 5913        view.move_left(&MoveLeft, cx);
 5914        view.move_left(&MoveLeft, cx);
 5915        view.backspace(&Default::default(), cx);
 5916    });
 5917
 5918    cx.assert_editor_state(
 5919        &"
 5920            {ˇ}
 5921            {ˇ}]]
 5922            {ˇ}
 5923        "
 5924        .unindent(),
 5925    );
 5926
 5927    cx.update_editor(|view, cx| {
 5928        view.backspace(&Default::default(), cx);
 5929    });
 5930
 5931    cx.assert_editor_state(
 5932        &"
 5933            ˇ
 5934            ˇ]]
 5935            ˇ
 5936        "
 5937        .unindent(),
 5938    );
 5939}
 5940
 5941#[gpui::test]
 5942async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5943    init_test(cx, |_| {});
 5944
 5945    let language = Arc::new(Language::new(
 5946        LanguageConfig::default(),
 5947        Some(tree_sitter_rust::language()),
 5948    ));
 5949
 5950    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5951    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5952    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5953    editor
 5954        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5955        .await;
 5956
 5957    _ = editor.update(cx, |editor, cx| {
 5958        editor.set_auto_replace_emoji_shortcode(true);
 5959
 5960        editor.handle_input("Hello ", cx);
 5961        editor.handle_input(":wave", cx);
 5962        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5963
 5964        editor.handle_input(":", cx);
 5965        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5966
 5967        editor.handle_input(" :smile", cx);
 5968        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5969
 5970        editor.handle_input(":", cx);
 5971        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5972
 5973        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5974        editor.handle_input(":wave", cx);
 5975        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5976
 5977        editor.handle_input(":", cx);
 5978        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5979
 5980        editor.handle_input(":1", cx);
 5981        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5982
 5983        editor.handle_input(":", cx);
 5984        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5985
 5986        // Ensure shortcode does not get replaced when it is part of a word
 5987        editor.handle_input(" Test:wave", cx);
 5988        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5989
 5990        editor.handle_input(":", cx);
 5991        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5992
 5993        editor.set_auto_replace_emoji_shortcode(false);
 5994
 5995        // Ensure shortcode does not get replaced when auto replace is off
 5996        editor.handle_input(" :wave", cx);
 5997        assert_eq!(
 5998            editor.text(cx),
 5999            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6000        );
 6001
 6002        editor.handle_input(":", cx);
 6003        assert_eq!(
 6004            editor.text(cx),
 6005            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6006        );
 6007    });
 6008}
 6009
 6010#[gpui::test]
 6011async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6012    init_test(cx, |_| {});
 6013
 6014    let (text, insertion_ranges) = marked_text_ranges(
 6015        indoc! {"
 6016            a.ˇ b
 6017            a.ˇ b
 6018            a.ˇ b
 6019        "},
 6020        false,
 6021    );
 6022
 6023    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6024    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6025
 6026    _ = editor.update(cx, |editor, cx| {
 6027        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6028
 6029        editor
 6030            .insert_snippet(&insertion_ranges, snippet, cx)
 6031            .unwrap();
 6032
 6033        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6034            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6035            assert_eq!(editor.text(cx), expected_text);
 6036            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6037        }
 6038
 6039        assert(
 6040            editor,
 6041            cx,
 6042            indoc! {"
 6043                a.f(«one», two, «three») b
 6044                a.f(«one», two, «three») b
 6045                a.f(«one», two, «three») b
 6046            "},
 6047        );
 6048
 6049        // Can't move earlier than the first tab stop
 6050        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6051        assert(
 6052            editor,
 6053            cx,
 6054            indoc! {"
 6055                a.f(«one», two, «three») b
 6056                a.f(«one», two, «three») b
 6057                a.f(«one», two, «three») b
 6058            "},
 6059        );
 6060
 6061        assert!(editor.move_to_next_snippet_tabstop(cx));
 6062        assert(
 6063            editor,
 6064            cx,
 6065            indoc! {"
 6066                a.f(one, «two», three) b
 6067                a.f(one, «two», three) b
 6068                a.f(one, «two», three) b
 6069            "},
 6070        );
 6071
 6072        editor.move_to_prev_snippet_tabstop(cx);
 6073        assert(
 6074            editor,
 6075            cx,
 6076            indoc! {"
 6077                a.f(«one», two, «three») b
 6078                a.f(«one», two, «three») b
 6079                a.f(«one», two, «three») b
 6080            "},
 6081        );
 6082
 6083        assert!(editor.move_to_next_snippet_tabstop(cx));
 6084        assert(
 6085            editor,
 6086            cx,
 6087            indoc! {"
 6088                a.f(one, «two», three) b
 6089                a.f(one, «two», three) b
 6090                a.f(one, «two», three) b
 6091            "},
 6092        );
 6093        assert!(editor.move_to_next_snippet_tabstop(cx));
 6094        assert(
 6095            editor,
 6096            cx,
 6097            indoc! {"
 6098                a.f(one, two, three)ˇ b
 6099                a.f(one, two, three)ˇ b
 6100                a.f(one, two, three)ˇ b
 6101            "},
 6102        );
 6103
 6104        // As soon as the last tab stop is reached, snippet state is gone
 6105        editor.move_to_prev_snippet_tabstop(cx);
 6106        assert(
 6107            editor,
 6108            cx,
 6109            indoc! {"
 6110                a.f(one, two, three)ˇ b
 6111                a.f(one, two, three)ˇ b
 6112                a.f(one, two, three)ˇ b
 6113            "},
 6114        );
 6115    });
 6116}
 6117
 6118#[gpui::test]
 6119async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6120    init_test(cx, |_| {});
 6121
 6122    let fs = FakeFs::new(cx.executor());
 6123    fs.insert_file("/file.rs", Default::default()).await;
 6124
 6125    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6126
 6127    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6128    language_registry.add(rust_lang());
 6129    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6130        "Rust",
 6131        FakeLspAdapter {
 6132            capabilities: lsp::ServerCapabilities {
 6133                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6134                ..Default::default()
 6135            },
 6136            ..Default::default()
 6137        },
 6138    );
 6139
 6140    let buffer = project
 6141        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6142        .await
 6143        .unwrap();
 6144
 6145    cx.executor().start_waiting();
 6146    let fake_server = fake_servers.next().await.unwrap();
 6147
 6148    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6149    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6150    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6151    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6152
 6153    let save = editor
 6154        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6155        .unwrap();
 6156    fake_server
 6157        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6158            assert_eq!(
 6159                params.text_document.uri,
 6160                lsp::Url::from_file_path("/file.rs").unwrap()
 6161            );
 6162            assert_eq!(params.options.tab_size, 4);
 6163            Ok(Some(vec![lsp::TextEdit::new(
 6164                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6165                ", ".to_string(),
 6166            )]))
 6167        })
 6168        .next()
 6169        .await;
 6170    cx.executor().start_waiting();
 6171    save.await;
 6172
 6173    assert_eq!(
 6174        editor.update(cx, |editor, cx| editor.text(cx)),
 6175        "one, two\nthree\n"
 6176    );
 6177    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6178
 6179    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6180    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6181
 6182    // Ensure we can still save even if formatting hangs.
 6183    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6184        assert_eq!(
 6185            params.text_document.uri,
 6186            lsp::Url::from_file_path("/file.rs").unwrap()
 6187        );
 6188        futures::future::pending::<()>().await;
 6189        unreachable!()
 6190    });
 6191    let save = editor
 6192        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6193        .unwrap();
 6194    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6195    cx.executor().start_waiting();
 6196    save.await;
 6197    assert_eq!(
 6198        editor.update(cx, |editor, cx| editor.text(cx)),
 6199        "one\ntwo\nthree\n"
 6200    );
 6201    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6202
 6203    // For non-dirty buffer, no formatting request should be sent
 6204    let save = editor
 6205        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6206        .unwrap();
 6207    let _pending_format_request = fake_server
 6208        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6209            panic!("Should not be invoked on non-dirty buffer");
 6210        })
 6211        .next();
 6212    cx.executor().start_waiting();
 6213    save.await;
 6214
 6215    // Set rust language override and assert overridden tabsize is sent to language server
 6216    update_test_language_settings(cx, |settings| {
 6217        settings.languages.insert(
 6218            "Rust".into(),
 6219            LanguageSettingsContent {
 6220                tab_size: NonZeroU32::new(8),
 6221                ..Default::default()
 6222            },
 6223        );
 6224    });
 6225
 6226    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6227    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6228    let save = editor
 6229        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6230        .unwrap();
 6231    fake_server
 6232        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6233            assert_eq!(
 6234                params.text_document.uri,
 6235                lsp::Url::from_file_path("/file.rs").unwrap()
 6236            );
 6237            assert_eq!(params.options.tab_size, 8);
 6238            Ok(Some(vec![]))
 6239        })
 6240        .next()
 6241        .await;
 6242    cx.executor().start_waiting();
 6243    save.await;
 6244}
 6245
 6246#[gpui::test]
 6247async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6248    init_test(cx, |_| {});
 6249
 6250    let cols = 4;
 6251    let rows = 10;
 6252    let sample_text_1 = sample_text(rows, cols, 'a');
 6253    assert_eq!(
 6254        sample_text_1,
 6255        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6256    );
 6257    let sample_text_2 = sample_text(rows, cols, 'l');
 6258    assert_eq!(
 6259        sample_text_2,
 6260        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6261    );
 6262    let sample_text_3 = sample_text(rows, cols, 'v');
 6263    assert_eq!(
 6264        sample_text_3,
 6265        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6266    );
 6267
 6268    let fs = FakeFs::new(cx.executor());
 6269    fs.insert_tree(
 6270        "/a",
 6271        json!({
 6272            "main.rs": sample_text_1,
 6273            "other.rs": sample_text_2,
 6274            "lib.rs": sample_text_3,
 6275        }),
 6276    )
 6277    .await;
 6278
 6279    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6280    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6281    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6282
 6283    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6284    language_registry.add(rust_lang());
 6285    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6286        "Rust",
 6287        FakeLspAdapter {
 6288            capabilities: lsp::ServerCapabilities {
 6289                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6290                ..Default::default()
 6291            },
 6292            ..Default::default()
 6293        },
 6294    );
 6295
 6296    let worktree = project.update(cx, |project, cx| {
 6297        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6298        assert_eq!(worktrees.len(), 1);
 6299        worktrees.pop().unwrap()
 6300    });
 6301    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6302
 6303    let buffer_1 = project
 6304        .update(cx, |project, cx| {
 6305            project.open_buffer((worktree_id, "main.rs"), cx)
 6306        })
 6307        .await
 6308        .unwrap();
 6309    let buffer_2 = project
 6310        .update(cx, |project, cx| {
 6311            project.open_buffer((worktree_id, "other.rs"), cx)
 6312        })
 6313        .await
 6314        .unwrap();
 6315    let buffer_3 = project
 6316        .update(cx, |project, cx| {
 6317            project.open_buffer((worktree_id, "lib.rs"), cx)
 6318        })
 6319        .await
 6320        .unwrap();
 6321
 6322    let multi_buffer = cx.new_model(|cx| {
 6323        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6324        multi_buffer.push_excerpts(
 6325            buffer_1.clone(),
 6326            [
 6327                ExcerptRange {
 6328                    context: Point::new(0, 0)..Point::new(3, 0),
 6329                    primary: None,
 6330                },
 6331                ExcerptRange {
 6332                    context: Point::new(5, 0)..Point::new(7, 0),
 6333                    primary: None,
 6334                },
 6335                ExcerptRange {
 6336                    context: Point::new(9, 0)..Point::new(10, 4),
 6337                    primary: None,
 6338                },
 6339            ],
 6340            cx,
 6341        );
 6342        multi_buffer.push_excerpts(
 6343            buffer_2.clone(),
 6344            [
 6345                ExcerptRange {
 6346                    context: Point::new(0, 0)..Point::new(3, 0),
 6347                    primary: None,
 6348                },
 6349                ExcerptRange {
 6350                    context: Point::new(5, 0)..Point::new(7, 0),
 6351                    primary: None,
 6352                },
 6353                ExcerptRange {
 6354                    context: Point::new(9, 0)..Point::new(10, 4),
 6355                    primary: None,
 6356                },
 6357            ],
 6358            cx,
 6359        );
 6360        multi_buffer.push_excerpts(
 6361            buffer_3.clone(),
 6362            [
 6363                ExcerptRange {
 6364                    context: Point::new(0, 0)..Point::new(3, 0),
 6365                    primary: None,
 6366                },
 6367                ExcerptRange {
 6368                    context: Point::new(5, 0)..Point::new(7, 0),
 6369                    primary: None,
 6370                },
 6371                ExcerptRange {
 6372                    context: Point::new(9, 0)..Point::new(10, 4),
 6373                    primary: None,
 6374                },
 6375            ],
 6376            cx,
 6377        );
 6378        multi_buffer
 6379    });
 6380    let multi_buffer_editor = cx.new_view(|cx| {
 6381        Editor::new(
 6382            EditorMode::Full,
 6383            multi_buffer,
 6384            Some(project.clone()),
 6385            true,
 6386            cx,
 6387        )
 6388    });
 6389
 6390    multi_buffer_editor.update(cx, |editor, cx| {
 6391        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6392        editor.insert("|one|two|three|", cx);
 6393    });
 6394    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6395    multi_buffer_editor.update(cx, |editor, cx| {
 6396        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6397            s.select_ranges(Some(60..70))
 6398        });
 6399        editor.insert("|four|five|six|", cx);
 6400    });
 6401    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6402
 6403    // First two buffers should be edited, but not the third one.
 6404    assert_eq!(
 6405        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6406        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6407    );
 6408    buffer_1.update(cx, |buffer, _| {
 6409        assert!(buffer.is_dirty());
 6410        assert_eq!(
 6411            buffer.text(),
 6412            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6413        )
 6414    });
 6415    buffer_2.update(cx, |buffer, _| {
 6416        assert!(buffer.is_dirty());
 6417        assert_eq!(
 6418            buffer.text(),
 6419            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6420        )
 6421    });
 6422    buffer_3.update(cx, |buffer, _| {
 6423        assert!(!buffer.is_dirty());
 6424        assert_eq!(buffer.text(), sample_text_3,)
 6425    });
 6426
 6427    cx.executor().start_waiting();
 6428    let save = multi_buffer_editor
 6429        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6430        .unwrap();
 6431
 6432    let fake_server = fake_servers.next().await.unwrap();
 6433    fake_server
 6434        .server
 6435        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6436            Ok(Some(vec![lsp::TextEdit::new(
 6437                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6438                format!("[{} formatted]", params.text_document.uri),
 6439            )]))
 6440        })
 6441        .detach();
 6442    save.await;
 6443
 6444    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6445    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6446    assert_eq!(
 6447        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6448        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6449    );
 6450    buffer_1.update(cx, |buffer, _| {
 6451        assert!(!buffer.is_dirty());
 6452        assert_eq!(
 6453            buffer.text(),
 6454            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6455        )
 6456    });
 6457    buffer_2.update(cx, |buffer, _| {
 6458        assert!(!buffer.is_dirty());
 6459        assert_eq!(
 6460            buffer.text(),
 6461            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6462        )
 6463    });
 6464    buffer_3.update(cx, |buffer, _| {
 6465        assert!(!buffer.is_dirty());
 6466        assert_eq!(buffer.text(), sample_text_3,)
 6467    });
 6468}
 6469
 6470#[gpui::test]
 6471async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6472    init_test(cx, |_| {});
 6473
 6474    let fs = FakeFs::new(cx.executor());
 6475    fs.insert_file("/file.rs", Default::default()).await;
 6476
 6477    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6478
 6479    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6480    language_registry.add(rust_lang());
 6481    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6482        "Rust",
 6483        FakeLspAdapter {
 6484            capabilities: lsp::ServerCapabilities {
 6485                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6486                ..Default::default()
 6487            },
 6488            ..Default::default()
 6489        },
 6490    );
 6491
 6492    let buffer = project
 6493        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6494        .await
 6495        .unwrap();
 6496
 6497    cx.executor().start_waiting();
 6498    let fake_server = fake_servers.next().await.unwrap();
 6499
 6500    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6501    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6502    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6503    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6504
 6505    let save = editor
 6506        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6507        .unwrap();
 6508    fake_server
 6509        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6510            assert_eq!(
 6511                params.text_document.uri,
 6512                lsp::Url::from_file_path("/file.rs").unwrap()
 6513            );
 6514            assert_eq!(params.options.tab_size, 4);
 6515            Ok(Some(vec![lsp::TextEdit::new(
 6516                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6517                ", ".to_string(),
 6518            )]))
 6519        })
 6520        .next()
 6521        .await;
 6522    cx.executor().start_waiting();
 6523    save.await;
 6524    assert_eq!(
 6525        editor.update(cx, |editor, cx| editor.text(cx)),
 6526        "one, two\nthree\n"
 6527    );
 6528    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6529
 6530    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6531    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6532
 6533    // Ensure we can still save even if formatting hangs.
 6534    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6535        move |params, _| async move {
 6536            assert_eq!(
 6537                params.text_document.uri,
 6538                lsp::Url::from_file_path("/file.rs").unwrap()
 6539            );
 6540            futures::future::pending::<()>().await;
 6541            unreachable!()
 6542        },
 6543    );
 6544    let save = editor
 6545        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6546        .unwrap();
 6547    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6548    cx.executor().start_waiting();
 6549    save.await;
 6550    assert_eq!(
 6551        editor.update(cx, |editor, cx| editor.text(cx)),
 6552        "one\ntwo\nthree\n"
 6553    );
 6554    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6555
 6556    // For non-dirty buffer, no formatting request should be sent
 6557    let save = editor
 6558        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6559        .unwrap();
 6560    let _pending_format_request = fake_server
 6561        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6562            panic!("Should not be invoked on non-dirty buffer");
 6563        })
 6564        .next();
 6565    cx.executor().start_waiting();
 6566    save.await;
 6567
 6568    // Set Rust language override and assert overridden tabsize is sent to language server
 6569    update_test_language_settings(cx, |settings| {
 6570        settings.languages.insert(
 6571            "Rust".into(),
 6572            LanguageSettingsContent {
 6573                tab_size: NonZeroU32::new(8),
 6574                ..Default::default()
 6575            },
 6576        );
 6577    });
 6578
 6579    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6580    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6581    let save = editor
 6582        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6583        .unwrap();
 6584    fake_server
 6585        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6586            assert_eq!(
 6587                params.text_document.uri,
 6588                lsp::Url::from_file_path("/file.rs").unwrap()
 6589            );
 6590            assert_eq!(params.options.tab_size, 8);
 6591            Ok(Some(vec![]))
 6592        })
 6593        .next()
 6594        .await;
 6595    cx.executor().start_waiting();
 6596    save.await;
 6597}
 6598
 6599#[gpui::test]
 6600async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6601    init_test(cx, |settings| {
 6602        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6603            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6604        ))
 6605    });
 6606
 6607    let fs = FakeFs::new(cx.executor());
 6608    fs.insert_file("/file.rs", Default::default()).await;
 6609
 6610    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6611
 6612    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6613    language_registry.add(Arc::new(Language::new(
 6614        LanguageConfig {
 6615            name: "Rust".into(),
 6616            matcher: LanguageMatcher {
 6617                path_suffixes: vec!["rs".to_string()],
 6618                ..Default::default()
 6619            },
 6620            ..LanguageConfig::default()
 6621        },
 6622        Some(tree_sitter_rust::language()),
 6623    )));
 6624    update_test_language_settings(cx, |settings| {
 6625        // Enable Prettier formatting for the same buffer, and ensure
 6626        // LSP is called instead of Prettier.
 6627        settings.defaults.prettier = Some(PrettierSettings {
 6628            allowed: true,
 6629            ..PrettierSettings::default()
 6630        });
 6631    });
 6632    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6633        "Rust",
 6634        FakeLspAdapter {
 6635            capabilities: lsp::ServerCapabilities {
 6636                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6637                ..Default::default()
 6638            },
 6639            ..Default::default()
 6640        },
 6641    );
 6642
 6643    let buffer = project
 6644        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6645        .await
 6646        .unwrap();
 6647
 6648    cx.executor().start_waiting();
 6649    let fake_server = fake_servers.next().await.unwrap();
 6650
 6651    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6652    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6653    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6654
 6655    let format = editor
 6656        .update(cx, |editor, cx| {
 6657            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6658        })
 6659        .unwrap();
 6660    fake_server
 6661        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6662            assert_eq!(
 6663                params.text_document.uri,
 6664                lsp::Url::from_file_path("/file.rs").unwrap()
 6665            );
 6666            assert_eq!(params.options.tab_size, 4);
 6667            Ok(Some(vec![lsp::TextEdit::new(
 6668                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6669                ", ".to_string(),
 6670            )]))
 6671        })
 6672        .next()
 6673        .await;
 6674    cx.executor().start_waiting();
 6675    format.await;
 6676    assert_eq!(
 6677        editor.update(cx, |editor, cx| editor.text(cx)),
 6678        "one, two\nthree\n"
 6679    );
 6680
 6681    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6682    // Ensure we don't lock if formatting hangs.
 6683    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6684        assert_eq!(
 6685            params.text_document.uri,
 6686            lsp::Url::from_file_path("/file.rs").unwrap()
 6687        );
 6688        futures::future::pending::<()>().await;
 6689        unreachable!()
 6690    });
 6691    let format = editor
 6692        .update(cx, |editor, cx| {
 6693            editor.perform_format(project, FormatTrigger::Manual, cx)
 6694        })
 6695        .unwrap();
 6696    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6697    cx.executor().start_waiting();
 6698    format.await;
 6699    assert_eq!(
 6700        editor.update(cx, |editor, cx| editor.text(cx)),
 6701        "one\ntwo\nthree\n"
 6702    );
 6703}
 6704
 6705#[gpui::test]
 6706async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6707    init_test(cx, |_| {});
 6708
 6709    let mut cx = EditorLspTestContext::new_rust(
 6710        lsp::ServerCapabilities {
 6711            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6712            ..Default::default()
 6713        },
 6714        cx,
 6715    )
 6716    .await;
 6717
 6718    cx.set_state(indoc! {"
 6719        one.twoˇ
 6720    "});
 6721
 6722    // The format request takes a long time. When it completes, it inserts
 6723    // a newline and an indent before the `.`
 6724    cx.lsp
 6725        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6726            let executor = cx.background_executor().clone();
 6727            async move {
 6728                executor.timer(Duration::from_millis(100)).await;
 6729                Ok(Some(vec![lsp::TextEdit {
 6730                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6731                    new_text: "\n    ".into(),
 6732                }]))
 6733            }
 6734        });
 6735
 6736    // Submit a format request.
 6737    let format_1 = cx
 6738        .update_editor(|editor, cx| editor.format(&Format, cx))
 6739        .unwrap();
 6740    cx.executor().run_until_parked();
 6741
 6742    // Submit a second format request.
 6743    let format_2 = cx
 6744        .update_editor(|editor, cx| editor.format(&Format, cx))
 6745        .unwrap();
 6746    cx.executor().run_until_parked();
 6747
 6748    // Wait for both format requests to complete
 6749    cx.executor().advance_clock(Duration::from_millis(200));
 6750    cx.executor().start_waiting();
 6751    format_1.await.unwrap();
 6752    cx.executor().start_waiting();
 6753    format_2.await.unwrap();
 6754
 6755    // The formatting edits only happens once.
 6756    cx.assert_editor_state(indoc! {"
 6757        one
 6758            .twoˇ
 6759    "});
 6760}
 6761
 6762#[gpui::test]
 6763async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6764    init_test(cx, |settings| {
 6765        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6766    });
 6767
 6768    let mut cx = EditorLspTestContext::new_rust(
 6769        lsp::ServerCapabilities {
 6770            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6771            ..Default::default()
 6772        },
 6773        cx,
 6774    )
 6775    .await;
 6776
 6777    // Set up a buffer white some trailing whitespace and no trailing newline.
 6778    cx.set_state(
 6779        &[
 6780            "one ",   //
 6781            "twoˇ",   //
 6782            "three ", //
 6783            "four",   //
 6784        ]
 6785        .join("\n"),
 6786    );
 6787
 6788    // Submit a format request.
 6789    let format = cx
 6790        .update_editor(|editor, cx| editor.format(&Format, cx))
 6791        .unwrap();
 6792
 6793    // Record which buffer changes have been sent to the language server
 6794    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6795    cx.lsp
 6796        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6797            let buffer_changes = buffer_changes.clone();
 6798            move |params, _| {
 6799                buffer_changes.lock().extend(
 6800                    params
 6801                        .content_changes
 6802                        .into_iter()
 6803                        .map(|e| (e.range.unwrap(), e.text)),
 6804                );
 6805            }
 6806        });
 6807
 6808    // Handle formatting requests to the language server.
 6809    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6810        let buffer_changes = buffer_changes.clone();
 6811        move |_, _| {
 6812            // When formatting is requested, trailing whitespace has already been stripped,
 6813            // and the trailing newline has already been added.
 6814            assert_eq!(
 6815                &buffer_changes.lock()[1..],
 6816                &[
 6817                    (
 6818                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6819                        "".into()
 6820                    ),
 6821                    (
 6822                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6823                        "".into()
 6824                    ),
 6825                    (
 6826                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6827                        "\n".into()
 6828                    ),
 6829                ]
 6830            );
 6831
 6832            // Insert blank lines between each line of the buffer.
 6833            async move {
 6834                Ok(Some(vec![
 6835                    lsp::TextEdit {
 6836                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6837                        new_text: "\n".into(),
 6838                    },
 6839                    lsp::TextEdit {
 6840                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6841                        new_text: "\n".into(),
 6842                    },
 6843                ]))
 6844            }
 6845        }
 6846    });
 6847
 6848    // After formatting the buffer, the trailing whitespace is stripped,
 6849    // a newline is appended, and the edits provided by the language server
 6850    // have been applied.
 6851    format.await.unwrap();
 6852    cx.assert_editor_state(
 6853        &[
 6854            "one",   //
 6855            "",      //
 6856            "twoˇ",  //
 6857            "",      //
 6858            "three", //
 6859            "four",  //
 6860            "",      //
 6861        ]
 6862        .join("\n"),
 6863    );
 6864
 6865    // Undoing the formatting undoes the trailing whitespace removal, the
 6866    // trailing newline, and the LSP edits.
 6867    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6868    cx.assert_editor_state(
 6869        &[
 6870            "one ",   //
 6871            "twoˇ",   //
 6872            "three ", //
 6873            "four",   //
 6874        ]
 6875        .join("\n"),
 6876    );
 6877}
 6878
 6879#[gpui::test]
 6880async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6881    cx: &mut gpui::TestAppContext,
 6882) {
 6883    init_test(cx, |_| {});
 6884
 6885    cx.update(|cx| {
 6886        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6887            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6888                settings.auto_signature_help = Some(true);
 6889            });
 6890        });
 6891    });
 6892
 6893    let mut cx = EditorLspTestContext::new_rust(
 6894        lsp::ServerCapabilities {
 6895            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6896                ..Default::default()
 6897            }),
 6898            ..Default::default()
 6899        },
 6900        cx,
 6901    )
 6902    .await;
 6903
 6904    let language = Language::new(
 6905        LanguageConfig {
 6906            name: "Rust".into(),
 6907            brackets: BracketPairConfig {
 6908                pairs: vec![
 6909                    BracketPair {
 6910                        start: "{".to_string(),
 6911                        end: "}".to_string(),
 6912                        close: true,
 6913                        surround: true,
 6914                        newline: true,
 6915                    },
 6916                    BracketPair {
 6917                        start: "(".to_string(),
 6918                        end: ")".to_string(),
 6919                        close: true,
 6920                        surround: true,
 6921                        newline: true,
 6922                    },
 6923                    BracketPair {
 6924                        start: "/*".to_string(),
 6925                        end: " */".to_string(),
 6926                        close: true,
 6927                        surround: true,
 6928                        newline: true,
 6929                    },
 6930                    BracketPair {
 6931                        start: "[".to_string(),
 6932                        end: "]".to_string(),
 6933                        close: false,
 6934                        surround: false,
 6935                        newline: true,
 6936                    },
 6937                    BracketPair {
 6938                        start: "\"".to_string(),
 6939                        end: "\"".to_string(),
 6940                        close: true,
 6941                        surround: true,
 6942                        newline: false,
 6943                    },
 6944                    BracketPair {
 6945                        start: "<".to_string(),
 6946                        end: ">".to_string(),
 6947                        close: false,
 6948                        surround: true,
 6949                        newline: true,
 6950                    },
 6951                ],
 6952                ..Default::default()
 6953            },
 6954            autoclose_before: "})]".to_string(),
 6955            ..Default::default()
 6956        },
 6957        Some(tree_sitter_rust::language()),
 6958    );
 6959    let language = Arc::new(language);
 6960
 6961    cx.language_registry().add(language.clone());
 6962    cx.update_buffer(|buffer, cx| {
 6963        buffer.set_language(Some(language), cx);
 6964    });
 6965
 6966    cx.set_state(
 6967        &r#"
 6968            fn main() {
 6969                sampleˇ
 6970            }
 6971        "#
 6972        .unindent(),
 6973    );
 6974
 6975    cx.update_editor(|view, cx| {
 6976        view.handle_input("(", cx);
 6977    });
 6978    cx.assert_editor_state(
 6979        &"
 6980            fn main() {
 6981                sample(ˇ)
 6982            }
 6983        "
 6984        .unindent(),
 6985    );
 6986
 6987    let mocked_response = lsp::SignatureHelp {
 6988        signatures: vec![lsp::SignatureInformation {
 6989            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6990            documentation: None,
 6991            parameters: Some(vec![
 6992                lsp::ParameterInformation {
 6993                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6994                    documentation: None,
 6995                },
 6996                lsp::ParameterInformation {
 6997                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6998                    documentation: None,
 6999                },
 7000            ]),
 7001            active_parameter: None,
 7002        }],
 7003        active_signature: Some(0),
 7004        active_parameter: Some(0),
 7005    };
 7006    handle_signature_help_request(&mut cx, mocked_response).await;
 7007
 7008    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7009        .await;
 7010
 7011    cx.editor(|editor, _| {
 7012        let signature_help_state = editor.signature_help_state.popover().cloned();
 7013        assert!(signature_help_state.is_some());
 7014        let ParsedMarkdown {
 7015            text, highlights, ..
 7016        } = signature_help_state.unwrap().parsed_content;
 7017        assert_eq!(text, "param1: u8, param2: u8");
 7018        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7019    });
 7020}
 7021
 7022#[gpui::test]
 7023async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7024    init_test(cx, |_| {});
 7025
 7026    cx.update(|cx| {
 7027        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7028            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7029                settings.auto_signature_help = Some(false);
 7030                settings.show_signature_help_after_edits = Some(false);
 7031            });
 7032        });
 7033    });
 7034
 7035    let mut cx = EditorLspTestContext::new_rust(
 7036        lsp::ServerCapabilities {
 7037            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7038                ..Default::default()
 7039            }),
 7040            ..Default::default()
 7041        },
 7042        cx,
 7043    )
 7044    .await;
 7045
 7046    let language = Language::new(
 7047        LanguageConfig {
 7048            name: "Rust".into(),
 7049            brackets: BracketPairConfig {
 7050                pairs: vec![
 7051                    BracketPair {
 7052                        start: "{".to_string(),
 7053                        end: "}".to_string(),
 7054                        close: true,
 7055                        surround: true,
 7056                        newline: true,
 7057                    },
 7058                    BracketPair {
 7059                        start: "(".to_string(),
 7060                        end: ")".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        newline: true,
 7064                    },
 7065                    BracketPair {
 7066                        start: "/*".to_string(),
 7067                        end: " */".to_string(),
 7068                        close: true,
 7069                        surround: true,
 7070                        newline: true,
 7071                    },
 7072                    BracketPair {
 7073                        start: "[".to_string(),
 7074                        end: "]".to_string(),
 7075                        close: false,
 7076                        surround: false,
 7077                        newline: true,
 7078                    },
 7079                    BracketPair {
 7080                        start: "\"".to_string(),
 7081                        end: "\"".to_string(),
 7082                        close: true,
 7083                        surround: true,
 7084                        newline: false,
 7085                    },
 7086                    BracketPair {
 7087                        start: "<".to_string(),
 7088                        end: ">".to_string(),
 7089                        close: false,
 7090                        surround: true,
 7091                        newline: true,
 7092                    },
 7093                ],
 7094                ..Default::default()
 7095            },
 7096            autoclose_before: "})]".to_string(),
 7097            ..Default::default()
 7098        },
 7099        Some(tree_sitter_rust::language()),
 7100    );
 7101    let language = Arc::new(language);
 7102
 7103    cx.language_registry().add(language.clone());
 7104    cx.update_buffer(|buffer, cx| {
 7105        buffer.set_language(Some(language), cx);
 7106    });
 7107
 7108    // Ensure that signature_help is not called when no signature help is enabled.
 7109    cx.set_state(
 7110        &r#"
 7111            fn main() {
 7112                sampleˇ
 7113            }
 7114        "#
 7115        .unindent(),
 7116    );
 7117    cx.update_editor(|view, cx| {
 7118        view.handle_input("(", cx);
 7119    });
 7120    cx.assert_editor_state(
 7121        &"
 7122            fn main() {
 7123                sample(ˇ)
 7124            }
 7125        "
 7126        .unindent(),
 7127    );
 7128    cx.editor(|editor, _| {
 7129        assert!(editor.signature_help_state.task().is_none());
 7130    });
 7131
 7132    let mocked_response = lsp::SignatureHelp {
 7133        signatures: vec![lsp::SignatureInformation {
 7134            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7135            documentation: None,
 7136            parameters: Some(vec![
 7137                lsp::ParameterInformation {
 7138                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7139                    documentation: None,
 7140                },
 7141                lsp::ParameterInformation {
 7142                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7143                    documentation: None,
 7144                },
 7145            ]),
 7146            active_parameter: None,
 7147        }],
 7148        active_signature: Some(0),
 7149        active_parameter: Some(0),
 7150    };
 7151
 7152    // Ensure that signature_help is called when enabled afte edits
 7153    cx.update(|cx| {
 7154        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7155            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7156                settings.auto_signature_help = Some(false);
 7157                settings.show_signature_help_after_edits = Some(true);
 7158            });
 7159        });
 7160    });
 7161    cx.set_state(
 7162        &r#"
 7163            fn main() {
 7164                sampleˇ
 7165            }
 7166        "#
 7167        .unindent(),
 7168    );
 7169    cx.update_editor(|view, cx| {
 7170        view.handle_input("(", cx);
 7171    });
 7172    cx.assert_editor_state(
 7173        &"
 7174            fn main() {
 7175                sample(ˇ)
 7176            }
 7177        "
 7178        .unindent(),
 7179    );
 7180    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7182        .await;
 7183    cx.update_editor(|editor, _| {
 7184        let signature_help_state = editor.signature_help_state.popover().cloned();
 7185        assert!(signature_help_state.is_some());
 7186        let ParsedMarkdown {
 7187            text, highlights, ..
 7188        } = signature_help_state.unwrap().parsed_content;
 7189        assert_eq!(text, "param1: u8, param2: u8");
 7190        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7191        editor.signature_help_state = SignatureHelpState::default();
 7192    });
 7193
 7194    // Ensure that signature_help is called when auto signature help override is enabled
 7195    cx.update(|cx| {
 7196        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7197            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7198                settings.auto_signature_help = Some(true);
 7199                settings.show_signature_help_after_edits = Some(false);
 7200            });
 7201        });
 7202    });
 7203    cx.set_state(
 7204        &r#"
 7205            fn main() {
 7206                sampleˇ
 7207            }
 7208        "#
 7209        .unindent(),
 7210    );
 7211    cx.update_editor(|view, cx| {
 7212        view.handle_input("(", cx);
 7213    });
 7214    cx.assert_editor_state(
 7215        &"
 7216            fn main() {
 7217                sample(ˇ)
 7218            }
 7219        "
 7220        .unindent(),
 7221    );
 7222    handle_signature_help_request(&mut cx, mocked_response).await;
 7223    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7224        .await;
 7225    cx.editor(|editor, _| {
 7226        let signature_help_state = editor.signature_help_state.popover().cloned();
 7227        assert!(signature_help_state.is_some());
 7228        let ParsedMarkdown {
 7229            text, highlights, ..
 7230        } = signature_help_state.unwrap().parsed_content;
 7231        assert_eq!(text, "param1: u8, param2: u8");
 7232        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7233    });
 7234}
 7235
 7236#[gpui::test]
 7237async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7238    init_test(cx, |_| {});
 7239    cx.update(|cx| {
 7240        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7241            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7242                settings.auto_signature_help = Some(true);
 7243            });
 7244        });
 7245    });
 7246
 7247    let mut cx = EditorLspTestContext::new_rust(
 7248        lsp::ServerCapabilities {
 7249            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7250                ..Default::default()
 7251            }),
 7252            ..Default::default()
 7253        },
 7254        cx,
 7255    )
 7256    .await;
 7257
 7258    // A test that directly calls `show_signature_help`
 7259    cx.update_editor(|editor, cx| {
 7260        editor.show_signature_help(&ShowSignatureHelp, cx);
 7261    });
 7262
 7263    let mocked_response = lsp::SignatureHelp {
 7264        signatures: vec![lsp::SignatureInformation {
 7265            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7266            documentation: None,
 7267            parameters: Some(vec![
 7268                lsp::ParameterInformation {
 7269                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7270                    documentation: None,
 7271                },
 7272                lsp::ParameterInformation {
 7273                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7274                    documentation: None,
 7275                },
 7276            ]),
 7277            active_parameter: None,
 7278        }],
 7279        active_signature: Some(0),
 7280        active_parameter: Some(0),
 7281    };
 7282    handle_signature_help_request(&mut cx, mocked_response).await;
 7283
 7284    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7285        .await;
 7286
 7287    cx.editor(|editor, _| {
 7288        let signature_help_state = editor.signature_help_state.popover().cloned();
 7289        assert!(signature_help_state.is_some());
 7290        let ParsedMarkdown {
 7291            text, highlights, ..
 7292        } = signature_help_state.unwrap().parsed_content;
 7293        assert_eq!(text, "param1: u8, param2: u8");
 7294        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7295    });
 7296
 7297    // When exiting outside from inside the brackets, `signature_help` is closed.
 7298    cx.set_state(indoc! {"
 7299        fn main() {
 7300            sample(ˇ);
 7301        }
 7302
 7303        fn sample(param1: u8, param2: u8) {}
 7304    "});
 7305
 7306    cx.update_editor(|editor, cx| {
 7307        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7308    });
 7309
 7310    let mocked_response = lsp::SignatureHelp {
 7311        signatures: Vec::new(),
 7312        active_signature: None,
 7313        active_parameter: None,
 7314    };
 7315    handle_signature_help_request(&mut cx, mocked_response).await;
 7316
 7317    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7318        .await;
 7319
 7320    cx.editor(|editor, _| {
 7321        assert!(!editor.signature_help_state.is_shown());
 7322    });
 7323
 7324    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7325    cx.set_state(indoc! {"
 7326        fn main() {
 7327            sample(ˇ);
 7328        }
 7329
 7330        fn sample(param1: u8, param2: u8) {}
 7331    "});
 7332
 7333    let mocked_response = lsp::SignatureHelp {
 7334        signatures: vec![lsp::SignatureInformation {
 7335            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7336            documentation: None,
 7337            parameters: Some(vec![
 7338                lsp::ParameterInformation {
 7339                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7340                    documentation: None,
 7341                },
 7342                lsp::ParameterInformation {
 7343                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7344                    documentation: None,
 7345                },
 7346            ]),
 7347            active_parameter: None,
 7348        }],
 7349        active_signature: Some(0),
 7350        active_parameter: Some(0),
 7351    };
 7352    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7353    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7354        .await;
 7355    cx.editor(|editor, _| {
 7356        assert!(editor.signature_help_state.is_shown());
 7357    });
 7358
 7359    // Restore the popover with more parameter input
 7360    cx.set_state(indoc! {"
 7361        fn main() {
 7362            sample(param1, param2ˇ);
 7363        }
 7364
 7365        fn sample(param1: u8, param2: u8) {}
 7366    "});
 7367
 7368    let mocked_response = lsp::SignatureHelp {
 7369        signatures: vec![lsp::SignatureInformation {
 7370            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7371            documentation: None,
 7372            parameters: Some(vec![
 7373                lsp::ParameterInformation {
 7374                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7375                    documentation: None,
 7376                },
 7377                lsp::ParameterInformation {
 7378                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7379                    documentation: None,
 7380                },
 7381            ]),
 7382            active_parameter: None,
 7383        }],
 7384        active_signature: Some(0),
 7385        active_parameter: Some(1),
 7386    };
 7387    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7388    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7389        .await;
 7390
 7391    // When selecting a range, the popover is gone.
 7392    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7393    cx.update_editor(|editor, cx| {
 7394        editor.change_selections(None, cx, |s| {
 7395            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7396        })
 7397    });
 7398    cx.assert_editor_state(indoc! {"
 7399        fn main() {
 7400            sample(param1, «ˇparam2»);
 7401        }
 7402
 7403        fn sample(param1: u8, param2: u8) {}
 7404    "});
 7405    cx.editor(|editor, _| {
 7406        assert!(!editor.signature_help_state.is_shown());
 7407    });
 7408
 7409    // When unselecting again, the popover is back if within the brackets.
 7410    cx.update_editor(|editor, cx| {
 7411        editor.change_selections(None, cx, |s| {
 7412            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7413        })
 7414    });
 7415    cx.assert_editor_state(indoc! {"
 7416        fn main() {
 7417            sample(param1, ˇparam2);
 7418        }
 7419
 7420        fn sample(param1: u8, param2: u8) {}
 7421    "});
 7422    handle_signature_help_request(&mut cx, mocked_response).await;
 7423    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7424        .await;
 7425    cx.editor(|editor, _| {
 7426        assert!(editor.signature_help_state.is_shown());
 7427    });
 7428
 7429    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7430    cx.update_editor(|editor, cx| {
 7431        editor.change_selections(None, cx, |s| {
 7432            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7433            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7434        })
 7435    });
 7436    cx.assert_editor_state(indoc! {"
 7437        fn main() {
 7438            sample(param1, ˇparam2);
 7439        }
 7440
 7441        fn sample(param1: u8, param2: u8) {}
 7442    "});
 7443
 7444    let mocked_response = lsp::SignatureHelp {
 7445        signatures: vec![lsp::SignatureInformation {
 7446            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7447            documentation: None,
 7448            parameters: Some(vec![
 7449                lsp::ParameterInformation {
 7450                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7451                    documentation: None,
 7452                },
 7453                lsp::ParameterInformation {
 7454                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7455                    documentation: None,
 7456                },
 7457            ]),
 7458            active_parameter: None,
 7459        }],
 7460        active_signature: Some(0),
 7461        active_parameter: Some(1),
 7462    };
 7463    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7464    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7465        .await;
 7466    cx.update_editor(|editor, cx| {
 7467        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7468    });
 7469    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7470        .await;
 7471    cx.update_editor(|editor, cx| {
 7472        editor.change_selections(None, cx, |s| {
 7473            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7474        })
 7475    });
 7476    cx.assert_editor_state(indoc! {"
 7477        fn main() {
 7478            sample(param1, «ˇparam2»);
 7479        }
 7480
 7481        fn sample(param1: u8, param2: u8) {}
 7482    "});
 7483    cx.update_editor(|editor, cx| {
 7484        editor.change_selections(None, cx, |s| {
 7485            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7486        })
 7487    });
 7488    cx.assert_editor_state(indoc! {"
 7489        fn main() {
 7490            sample(param1, ˇparam2);
 7491        }
 7492
 7493        fn sample(param1: u8, param2: u8) {}
 7494    "});
 7495    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7496        .await;
 7497}
 7498
 7499#[gpui::test]
 7500async fn test_completion(cx: &mut gpui::TestAppContext) {
 7501    init_test(cx, |_| {});
 7502
 7503    let mut cx = EditorLspTestContext::new_rust(
 7504        lsp::ServerCapabilities {
 7505            completion_provider: Some(lsp::CompletionOptions {
 7506                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7507                resolve_provider: Some(true),
 7508                ..Default::default()
 7509            }),
 7510            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7511            ..Default::default()
 7512        },
 7513        cx,
 7514    )
 7515    .await;
 7516    let counter = Arc::new(AtomicUsize::new(0));
 7517
 7518    cx.set_state(indoc! {"
 7519        oneˇ
 7520        two
 7521        three
 7522    "});
 7523    cx.simulate_keystroke(".");
 7524    handle_completion_request(
 7525        &mut cx,
 7526        indoc! {"
 7527            one.|<>
 7528            two
 7529            three
 7530        "},
 7531        vec!["first_completion", "second_completion"],
 7532        counter.clone(),
 7533    )
 7534    .await;
 7535    cx.condition(|editor, _| editor.context_menu_visible())
 7536        .await;
 7537    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7538
 7539    let _handler = handle_signature_help_request(
 7540        &mut cx,
 7541        lsp::SignatureHelp {
 7542            signatures: vec![lsp::SignatureInformation {
 7543                label: "test signature".to_string(),
 7544                documentation: None,
 7545                parameters: Some(vec![lsp::ParameterInformation {
 7546                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7547                    documentation: None,
 7548                }]),
 7549                active_parameter: None,
 7550            }],
 7551            active_signature: None,
 7552            active_parameter: None,
 7553        },
 7554    );
 7555    cx.update_editor(|editor, cx| {
 7556        assert!(
 7557            !editor.signature_help_state.is_shown(),
 7558            "No signature help was called for"
 7559        );
 7560        editor.show_signature_help(&ShowSignatureHelp, cx);
 7561    });
 7562    cx.run_until_parked();
 7563    cx.update_editor(|editor, _| {
 7564        assert!(
 7565            !editor.signature_help_state.is_shown(),
 7566            "No signature help should be shown when completions menu is open"
 7567        );
 7568    });
 7569
 7570    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7571        editor.context_menu_next(&Default::default(), cx);
 7572        editor
 7573            .confirm_completion(&ConfirmCompletion::default(), cx)
 7574            .unwrap()
 7575    });
 7576    cx.assert_editor_state(indoc! {"
 7577        one.second_completionˇ
 7578        two
 7579        three
 7580    "});
 7581
 7582    handle_resolve_completion_request(
 7583        &mut cx,
 7584        Some(vec![
 7585            (
 7586                //This overlaps with the primary completion edit which is
 7587                //misbehavior from the LSP spec, test that we filter it out
 7588                indoc! {"
 7589                    one.second_ˇcompletion
 7590                    two
 7591                    threeˇ
 7592                "},
 7593                "overlapping additional edit",
 7594            ),
 7595            (
 7596                indoc! {"
 7597                    one.second_completion
 7598                    two
 7599                    threeˇ
 7600                "},
 7601                "\nadditional edit",
 7602            ),
 7603        ]),
 7604    )
 7605    .await;
 7606    apply_additional_edits.await.unwrap();
 7607    cx.assert_editor_state(indoc! {"
 7608        one.second_completionˇ
 7609        two
 7610        three
 7611        additional edit
 7612    "});
 7613
 7614    cx.set_state(indoc! {"
 7615        one.second_completion
 7616        twoˇ
 7617        threeˇ
 7618        additional edit
 7619    "});
 7620    cx.simulate_keystroke(" ");
 7621    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7622    cx.simulate_keystroke("s");
 7623    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7624
 7625    cx.assert_editor_state(indoc! {"
 7626        one.second_completion
 7627        two sˇ
 7628        three sˇ
 7629        additional edit
 7630    "});
 7631    handle_completion_request(
 7632        &mut cx,
 7633        indoc! {"
 7634            one.second_completion
 7635            two s
 7636            three <s|>
 7637            additional edit
 7638        "},
 7639        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7640        counter.clone(),
 7641    )
 7642    .await;
 7643    cx.condition(|editor, _| editor.context_menu_visible())
 7644        .await;
 7645    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7646
 7647    cx.simulate_keystroke("i");
 7648
 7649    handle_completion_request(
 7650        &mut cx,
 7651        indoc! {"
 7652            one.second_completion
 7653            two si
 7654            three <si|>
 7655            additional edit
 7656        "},
 7657        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7658        counter.clone(),
 7659    )
 7660    .await;
 7661    cx.condition(|editor, _| editor.context_menu_visible())
 7662        .await;
 7663    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7664
 7665    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7666        editor
 7667            .confirm_completion(&ConfirmCompletion::default(), cx)
 7668            .unwrap()
 7669    });
 7670    cx.assert_editor_state(indoc! {"
 7671        one.second_completion
 7672        two sixth_completionˇ
 7673        three sixth_completionˇ
 7674        additional edit
 7675    "});
 7676
 7677    handle_resolve_completion_request(&mut cx, None).await;
 7678    apply_additional_edits.await.unwrap();
 7679
 7680    _ = cx.update(|cx| {
 7681        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7682            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7683                settings.show_completions_on_input = Some(false);
 7684            });
 7685        })
 7686    });
 7687    cx.set_state("editorˇ");
 7688    cx.simulate_keystroke(".");
 7689    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7690    cx.simulate_keystroke("c");
 7691    cx.simulate_keystroke("l");
 7692    cx.simulate_keystroke("o");
 7693    cx.assert_editor_state("editor.cloˇ");
 7694    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7695    cx.update_editor(|editor, cx| {
 7696        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7697    });
 7698    handle_completion_request(
 7699        &mut cx,
 7700        "editor.<clo|>",
 7701        vec!["close", "clobber"],
 7702        counter.clone(),
 7703    )
 7704    .await;
 7705    cx.condition(|editor, _| editor.context_menu_visible())
 7706        .await;
 7707    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7708
 7709    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7710        editor
 7711            .confirm_completion(&ConfirmCompletion::default(), cx)
 7712            .unwrap()
 7713    });
 7714    cx.assert_editor_state("editor.closeˇ");
 7715    handle_resolve_completion_request(&mut cx, None).await;
 7716    apply_additional_edits.await.unwrap();
 7717}
 7718
 7719#[gpui::test]
 7720async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7721    init_test(cx, |_| {});
 7722    let mut cx = EditorLspTestContext::new_rust(
 7723        lsp::ServerCapabilities {
 7724            completion_provider: Some(lsp::CompletionOptions {
 7725                trigger_characters: Some(vec![".".to_string()]),
 7726                ..Default::default()
 7727            }),
 7728            ..Default::default()
 7729        },
 7730        cx,
 7731    )
 7732    .await;
 7733    cx.lsp
 7734        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7735            Ok(Some(lsp::CompletionResponse::Array(vec![
 7736                lsp::CompletionItem {
 7737                    label: "first".into(),
 7738                    ..Default::default()
 7739                },
 7740                lsp::CompletionItem {
 7741                    label: "last".into(),
 7742                    ..Default::default()
 7743                },
 7744            ])))
 7745        });
 7746    cx.set_state("variableˇ");
 7747    cx.simulate_keystroke(".");
 7748    cx.executor().run_until_parked();
 7749
 7750    cx.update_editor(|editor, _| {
 7751        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7752            assert_eq!(
 7753                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7754                &["first", "last"]
 7755            );
 7756        } else {
 7757            panic!("expected completion menu to be open");
 7758        }
 7759    });
 7760
 7761    cx.update_editor(|editor, cx| {
 7762        editor.move_page_down(&MovePageDown::default(), cx);
 7763        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7764            assert!(
 7765                menu.selected_item == 1,
 7766                "expected PageDown to select the last item from the context menu"
 7767            );
 7768        } else {
 7769            panic!("expected completion menu to stay open after PageDown");
 7770        }
 7771    });
 7772
 7773    cx.update_editor(|editor, cx| {
 7774        editor.move_page_up(&MovePageUp::default(), cx);
 7775        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7776            assert!(
 7777                menu.selected_item == 0,
 7778                "expected PageUp to select the first item from the context menu"
 7779            );
 7780        } else {
 7781            panic!("expected completion menu to stay open after PageUp");
 7782        }
 7783    });
 7784}
 7785
 7786#[gpui::test]
 7787async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7788    init_test(cx, |_| {});
 7789
 7790    let mut cx = EditorLspTestContext::new_rust(
 7791        lsp::ServerCapabilities {
 7792            completion_provider: Some(lsp::CompletionOptions {
 7793                trigger_characters: Some(vec![".".to_string()]),
 7794                resolve_provider: Some(true),
 7795                ..Default::default()
 7796            }),
 7797            ..Default::default()
 7798        },
 7799        cx,
 7800    )
 7801    .await;
 7802
 7803    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7804    cx.simulate_keystroke(".");
 7805    let completion_item = lsp::CompletionItem {
 7806        label: "Some".into(),
 7807        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7808        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7809        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7810            kind: lsp::MarkupKind::Markdown,
 7811            value: "```rust\nSome(2)\n```".to_string(),
 7812        })),
 7813        deprecated: Some(false),
 7814        sort_text: Some("Some".to_string()),
 7815        filter_text: Some("Some".to_string()),
 7816        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7817        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7818            range: lsp::Range {
 7819                start: lsp::Position {
 7820                    line: 0,
 7821                    character: 22,
 7822                },
 7823                end: lsp::Position {
 7824                    line: 0,
 7825                    character: 22,
 7826                },
 7827            },
 7828            new_text: "Some(2)".to_string(),
 7829        })),
 7830        additional_text_edits: Some(vec![lsp::TextEdit {
 7831            range: lsp::Range {
 7832                start: lsp::Position {
 7833                    line: 0,
 7834                    character: 20,
 7835                },
 7836                end: lsp::Position {
 7837                    line: 0,
 7838                    character: 22,
 7839                },
 7840            },
 7841            new_text: "".to_string(),
 7842        }]),
 7843        ..Default::default()
 7844    };
 7845
 7846    let closure_completion_item = completion_item.clone();
 7847    let counter = Arc::new(AtomicUsize::new(0));
 7848    let counter_clone = counter.clone();
 7849    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7850        let task_completion_item = closure_completion_item.clone();
 7851        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7852        async move {
 7853            Ok(Some(lsp::CompletionResponse::Array(vec![
 7854                task_completion_item,
 7855            ])))
 7856        }
 7857    });
 7858
 7859    cx.condition(|editor, _| editor.context_menu_visible())
 7860        .await;
 7861    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7862    assert!(request.next().await.is_some());
 7863    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7864
 7865    cx.simulate_keystroke("S");
 7866    cx.simulate_keystroke("o");
 7867    cx.simulate_keystroke("m");
 7868    cx.condition(|editor, _| editor.context_menu_visible())
 7869        .await;
 7870    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7871    assert!(request.next().await.is_some());
 7872    assert!(request.next().await.is_some());
 7873    assert!(request.next().await.is_some());
 7874    request.close();
 7875    assert!(request.next().await.is_none());
 7876    assert_eq!(
 7877        counter.load(atomic::Ordering::Acquire),
 7878        4,
 7879        "With the completions menu open, only one LSP request should happen per input"
 7880    );
 7881}
 7882
 7883#[gpui::test]
 7884async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7885    init_test(cx, |_| {});
 7886    let mut cx = EditorTestContext::new(cx).await;
 7887    let language = Arc::new(Language::new(
 7888        LanguageConfig {
 7889            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7890            ..Default::default()
 7891        },
 7892        Some(tree_sitter_rust::language()),
 7893    ));
 7894    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7895
 7896    // If multiple selections intersect a line, the line is only toggled once.
 7897    cx.set_state(indoc! {"
 7898        fn a() {
 7899            «//b();
 7900            ˇ»// «c();
 7901            //ˇ»  d();
 7902        }
 7903    "});
 7904
 7905    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7906
 7907    cx.assert_editor_state(indoc! {"
 7908        fn a() {
 7909            «b();
 7910            c();
 7911            ˇ» d();
 7912        }
 7913    "});
 7914
 7915    // The comment prefix is inserted at the same column for every line in a
 7916    // selection.
 7917    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7918
 7919    cx.assert_editor_state(indoc! {"
 7920        fn a() {
 7921            // «b();
 7922            // c();
 7923            ˇ»//  d();
 7924        }
 7925    "});
 7926
 7927    // If a selection ends at the beginning of a line, that line is not toggled.
 7928    cx.set_selections_state(indoc! {"
 7929        fn a() {
 7930            // b();
 7931            «// c();
 7932        ˇ»    //  d();
 7933        }
 7934    "});
 7935
 7936    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7937
 7938    cx.assert_editor_state(indoc! {"
 7939        fn a() {
 7940            // b();
 7941            «c();
 7942        ˇ»    //  d();
 7943        }
 7944    "});
 7945
 7946    // If a selection span a single line and is empty, the line is toggled.
 7947    cx.set_state(indoc! {"
 7948        fn a() {
 7949            a();
 7950            b();
 7951        ˇ
 7952        }
 7953    "});
 7954
 7955    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7956
 7957    cx.assert_editor_state(indoc! {"
 7958        fn a() {
 7959            a();
 7960            b();
 7961        //•ˇ
 7962        }
 7963    "});
 7964
 7965    // If a selection span multiple lines, empty lines are not toggled.
 7966    cx.set_state(indoc! {"
 7967        fn a() {
 7968            «a();
 7969
 7970            c();ˇ»
 7971        }
 7972    "});
 7973
 7974    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7975
 7976    cx.assert_editor_state(indoc! {"
 7977        fn a() {
 7978            // «a();
 7979
 7980            // c();ˇ»
 7981        }
 7982    "});
 7983
 7984    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7985    cx.set_state(indoc! {"
 7986        fn a() {
 7987            «// a();
 7988            /// b();
 7989            //! c();ˇ»
 7990        }
 7991    "});
 7992
 7993    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7994
 7995    cx.assert_editor_state(indoc! {"
 7996        fn a() {
 7997            «a();
 7998            b();
 7999            c();ˇ»
 8000        }
 8001    "});
 8002}
 8003
 8004#[gpui::test]
 8005async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8006    init_test(cx, |_| {});
 8007
 8008    let language = Arc::new(Language::new(
 8009        LanguageConfig {
 8010            line_comments: vec!["// ".into()],
 8011            ..Default::default()
 8012        },
 8013        Some(tree_sitter_rust::language()),
 8014    ));
 8015
 8016    let mut cx = EditorTestContext::new(cx).await;
 8017
 8018    cx.language_registry().add(language.clone());
 8019    cx.update_buffer(|buffer, cx| {
 8020        buffer.set_language(Some(language), cx);
 8021    });
 8022
 8023    let toggle_comments = &ToggleComments {
 8024        advance_downwards: true,
 8025    };
 8026
 8027    // Single cursor on one line -> advance
 8028    // Cursor moves horizontally 3 characters as well on non-blank line
 8029    cx.set_state(indoc!(
 8030        "fn a() {
 8031             ˇdog();
 8032             cat();
 8033        }"
 8034    ));
 8035    cx.update_editor(|editor, cx| {
 8036        editor.toggle_comments(toggle_comments, cx);
 8037    });
 8038    cx.assert_editor_state(indoc!(
 8039        "fn a() {
 8040             // dog();
 8041             catˇ();
 8042        }"
 8043    ));
 8044
 8045    // Single selection on one line -> don't advance
 8046    cx.set_state(indoc!(
 8047        "fn a() {
 8048             «dog()ˇ»;
 8049             cat();
 8050        }"
 8051    ));
 8052    cx.update_editor(|editor, cx| {
 8053        editor.toggle_comments(toggle_comments, cx);
 8054    });
 8055    cx.assert_editor_state(indoc!(
 8056        "fn a() {
 8057             // «dog()ˇ»;
 8058             cat();
 8059        }"
 8060    ));
 8061
 8062    // Multiple cursors on one line -> advance
 8063    cx.set_state(indoc!(
 8064        "fn a() {
 8065             ˇdˇog();
 8066             cat();
 8067        }"
 8068    ));
 8069    cx.update_editor(|editor, cx| {
 8070        editor.toggle_comments(toggle_comments, cx);
 8071    });
 8072    cx.assert_editor_state(indoc!(
 8073        "fn a() {
 8074             // dog();
 8075             catˇ(ˇ);
 8076        }"
 8077    ));
 8078
 8079    // Multiple cursors on one line, with selection -> don't advance
 8080    cx.set_state(indoc!(
 8081        "fn a() {
 8082             ˇdˇog«()ˇ»;
 8083             cat();
 8084        }"
 8085    ));
 8086    cx.update_editor(|editor, cx| {
 8087        editor.toggle_comments(toggle_comments, cx);
 8088    });
 8089    cx.assert_editor_state(indoc!(
 8090        "fn a() {
 8091             // ˇdˇog«()ˇ»;
 8092             cat();
 8093        }"
 8094    ));
 8095
 8096    // Single cursor on one line -> advance
 8097    // Cursor moves to column 0 on blank line
 8098    cx.set_state(indoc!(
 8099        "fn a() {
 8100             ˇdog();
 8101
 8102             cat();
 8103        }"
 8104    ));
 8105    cx.update_editor(|editor, cx| {
 8106        editor.toggle_comments(toggle_comments, cx);
 8107    });
 8108    cx.assert_editor_state(indoc!(
 8109        "fn a() {
 8110             // dog();
 8111        ˇ
 8112             cat();
 8113        }"
 8114    ));
 8115
 8116    // Single cursor on one line -> advance
 8117    // Cursor starts and ends at column 0
 8118    cx.set_state(indoc!(
 8119        "fn a() {
 8120         ˇ    dog();
 8121             cat();
 8122        }"
 8123    ));
 8124    cx.update_editor(|editor, cx| {
 8125        editor.toggle_comments(toggle_comments, cx);
 8126    });
 8127    cx.assert_editor_state(indoc!(
 8128        "fn a() {
 8129             // dog();
 8130         ˇ    cat();
 8131        }"
 8132    ));
 8133}
 8134
 8135#[gpui::test]
 8136async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8137    init_test(cx, |_| {});
 8138
 8139    let mut cx = EditorTestContext::new(cx).await;
 8140
 8141    let html_language = Arc::new(
 8142        Language::new(
 8143            LanguageConfig {
 8144                name: "HTML".into(),
 8145                block_comment: Some(("<!-- ".into(), " -->".into())),
 8146                ..Default::default()
 8147            },
 8148            Some(tree_sitter_html::language()),
 8149        )
 8150        .with_injection_query(
 8151            r#"
 8152            (script_element
 8153                (raw_text) @content
 8154                (#set! "language" "javascript"))
 8155            "#,
 8156        )
 8157        .unwrap(),
 8158    );
 8159
 8160    let javascript_language = Arc::new(Language::new(
 8161        LanguageConfig {
 8162            name: "JavaScript".into(),
 8163            line_comments: vec!["// ".into()],
 8164            ..Default::default()
 8165        },
 8166        Some(tree_sitter_typescript::language_tsx()),
 8167    ));
 8168
 8169    cx.language_registry().add(html_language.clone());
 8170    cx.language_registry().add(javascript_language.clone());
 8171    cx.update_buffer(|buffer, cx| {
 8172        buffer.set_language(Some(html_language), cx);
 8173    });
 8174
 8175    // Toggle comments for empty selections
 8176    cx.set_state(
 8177        &r#"
 8178            <p>A</p>ˇ
 8179            <p>B</p>ˇ
 8180            <p>C</p>ˇ
 8181        "#
 8182        .unindent(),
 8183    );
 8184    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8185    cx.assert_editor_state(
 8186        &r#"
 8187            <!-- <p>A</p>ˇ -->
 8188            <!-- <p>B</p>ˇ -->
 8189            <!-- <p>C</p>ˇ -->
 8190        "#
 8191        .unindent(),
 8192    );
 8193    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8194    cx.assert_editor_state(
 8195        &r#"
 8196            <p>A</p>ˇ
 8197            <p>B</p>ˇ
 8198            <p>C</p>ˇ
 8199        "#
 8200        .unindent(),
 8201    );
 8202
 8203    // Toggle comments for mixture of empty and non-empty selections, where
 8204    // multiple selections occupy a given line.
 8205    cx.set_state(
 8206        &r#"
 8207            <p>A«</p>
 8208            <p>ˇ»B</p>ˇ
 8209            <p>C«</p>
 8210            <p>ˇ»D</p>ˇ
 8211        "#
 8212        .unindent(),
 8213    );
 8214
 8215    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8216    cx.assert_editor_state(
 8217        &r#"
 8218            <!-- <p>A«</p>
 8219            <p>ˇ»B</p>ˇ -->
 8220            <!-- <p>C«</p>
 8221            <p>ˇ»D</p>ˇ -->
 8222        "#
 8223        .unindent(),
 8224    );
 8225    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8226    cx.assert_editor_state(
 8227        &r#"
 8228            <p>A«</p>
 8229            <p>ˇ»B</p>ˇ
 8230            <p>C«</p>
 8231            <p>ˇ»D</p>ˇ
 8232        "#
 8233        .unindent(),
 8234    );
 8235
 8236    // Toggle comments when different languages are active for different
 8237    // selections.
 8238    cx.set_state(
 8239        &r#"
 8240            ˇ<script>
 8241                ˇvar x = new Y();
 8242            ˇ</script>
 8243        "#
 8244        .unindent(),
 8245    );
 8246    cx.executor().run_until_parked();
 8247    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8248    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8249    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8250    cx.assert_editor_state(
 8251        &r#"
 8252            <!-- ˇ<script> -->
 8253                // ˇvar x = new Y();
 8254            // ˇ</script>
 8255        "#
 8256        .unindent(),
 8257    );
 8258}
 8259
 8260#[gpui::test]
 8261fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8262    init_test(cx, |_| {});
 8263
 8264    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8265    let multibuffer = cx.new_model(|cx| {
 8266        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8267        multibuffer.push_excerpts(
 8268            buffer.clone(),
 8269            [
 8270                ExcerptRange {
 8271                    context: Point::new(0, 0)..Point::new(0, 4),
 8272                    primary: None,
 8273                },
 8274                ExcerptRange {
 8275                    context: Point::new(1, 0)..Point::new(1, 4),
 8276                    primary: None,
 8277                },
 8278            ],
 8279            cx,
 8280        );
 8281        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8282        multibuffer
 8283    });
 8284
 8285    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8286    _ = view.update(cx, |view, cx| {
 8287        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8288        view.change_selections(None, cx, |s| {
 8289            s.select_ranges([
 8290                Point::new(0, 0)..Point::new(0, 0),
 8291                Point::new(1, 0)..Point::new(1, 0),
 8292            ])
 8293        });
 8294
 8295        view.handle_input("X", cx);
 8296        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8297        assert_eq!(
 8298            view.selections.ranges(cx),
 8299            [
 8300                Point::new(0, 1)..Point::new(0, 1),
 8301                Point::new(1, 1)..Point::new(1, 1),
 8302            ]
 8303        );
 8304
 8305        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8306        view.change_selections(None, cx, |s| {
 8307            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8308        });
 8309        view.backspace(&Default::default(), cx);
 8310        assert_eq!(view.text(cx), "Xa\nbbb");
 8311        assert_eq!(
 8312            view.selections.ranges(cx),
 8313            [Point::new(1, 0)..Point::new(1, 0)]
 8314        );
 8315
 8316        view.change_selections(None, cx, |s| {
 8317            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8318        });
 8319        view.backspace(&Default::default(), cx);
 8320        assert_eq!(view.text(cx), "X\nbb");
 8321        assert_eq!(
 8322            view.selections.ranges(cx),
 8323            [Point::new(0, 1)..Point::new(0, 1)]
 8324        );
 8325    });
 8326}
 8327
 8328#[gpui::test]
 8329fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8330    init_test(cx, |_| {});
 8331
 8332    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8333    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8334        indoc! {"
 8335            [aaaa
 8336            (bbbb]
 8337            cccc)",
 8338        },
 8339        markers.clone(),
 8340    );
 8341    let excerpt_ranges = markers.into_iter().map(|marker| {
 8342        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8343        ExcerptRange {
 8344            context,
 8345            primary: None,
 8346        }
 8347    });
 8348    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8349    let multibuffer = cx.new_model(|cx| {
 8350        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8351        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8352        multibuffer
 8353    });
 8354
 8355    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8356    _ = view.update(cx, |view, cx| {
 8357        let (expected_text, selection_ranges) = marked_text_ranges(
 8358            indoc! {"
 8359                aaaa
 8360                bˇbbb
 8361                bˇbbˇb
 8362                cccc"
 8363            },
 8364            true,
 8365        );
 8366        assert_eq!(view.text(cx), expected_text);
 8367        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8368
 8369        view.handle_input("X", cx);
 8370
 8371        let (expected_text, expected_selections) = marked_text_ranges(
 8372            indoc! {"
 8373                aaaa
 8374                bXˇbbXb
 8375                bXˇbbXˇb
 8376                cccc"
 8377            },
 8378            false,
 8379        );
 8380        assert_eq!(view.text(cx), expected_text);
 8381        assert_eq!(view.selections.ranges(cx), expected_selections);
 8382
 8383        view.newline(&Newline, cx);
 8384        let (expected_text, expected_selections) = marked_text_ranges(
 8385            indoc! {"
 8386                aaaa
 8387                bX
 8388                ˇbbX
 8389                b
 8390                bX
 8391                ˇbbX
 8392                ˇb
 8393                cccc"
 8394            },
 8395            false,
 8396        );
 8397        assert_eq!(view.text(cx), expected_text);
 8398        assert_eq!(view.selections.ranges(cx), expected_selections);
 8399    });
 8400}
 8401
 8402#[gpui::test]
 8403fn test_refresh_selections(cx: &mut TestAppContext) {
 8404    init_test(cx, |_| {});
 8405
 8406    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8407    let mut excerpt1_id = None;
 8408    let multibuffer = cx.new_model(|cx| {
 8409        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8410        excerpt1_id = multibuffer
 8411            .push_excerpts(
 8412                buffer.clone(),
 8413                [
 8414                    ExcerptRange {
 8415                        context: Point::new(0, 0)..Point::new(1, 4),
 8416                        primary: None,
 8417                    },
 8418                    ExcerptRange {
 8419                        context: Point::new(1, 0)..Point::new(2, 4),
 8420                        primary: None,
 8421                    },
 8422                ],
 8423                cx,
 8424            )
 8425            .into_iter()
 8426            .next();
 8427        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8428        multibuffer
 8429    });
 8430
 8431    let editor = cx.add_window(|cx| {
 8432        let mut editor = build_editor(multibuffer.clone(), cx);
 8433        let snapshot = editor.snapshot(cx);
 8434        editor.change_selections(None, cx, |s| {
 8435            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8436        });
 8437        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8438        assert_eq!(
 8439            editor.selections.ranges(cx),
 8440            [
 8441                Point::new(1, 3)..Point::new(1, 3),
 8442                Point::new(2, 1)..Point::new(2, 1),
 8443            ]
 8444        );
 8445        editor
 8446    });
 8447
 8448    // Refreshing selections is a no-op when excerpts haven't changed.
 8449    _ = editor.update(cx, |editor, cx| {
 8450        editor.change_selections(None, cx, |s| s.refresh());
 8451        assert_eq!(
 8452            editor.selections.ranges(cx),
 8453            [
 8454                Point::new(1, 3)..Point::new(1, 3),
 8455                Point::new(2, 1)..Point::new(2, 1),
 8456            ]
 8457        );
 8458    });
 8459
 8460    _ = multibuffer.update(cx, |multibuffer, cx| {
 8461        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8462    });
 8463    _ = editor.update(cx, |editor, cx| {
 8464        // Removing an excerpt causes the first selection to become degenerate.
 8465        assert_eq!(
 8466            editor.selections.ranges(cx),
 8467            [
 8468                Point::new(0, 0)..Point::new(0, 0),
 8469                Point::new(0, 1)..Point::new(0, 1)
 8470            ]
 8471        );
 8472
 8473        // Refreshing selections will relocate the first selection to the original buffer
 8474        // location.
 8475        editor.change_selections(None, cx, |s| s.refresh());
 8476        assert_eq!(
 8477            editor.selections.ranges(cx),
 8478            [
 8479                Point::new(0, 1)..Point::new(0, 1),
 8480                Point::new(0, 3)..Point::new(0, 3)
 8481            ]
 8482        );
 8483        assert!(editor.selections.pending_anchor().is_some());
 8484    });
 8485}
 8486
 8487#[gpui::test]
 8488fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8489    init_test(cx, |_| {});
 8490
 8491    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8492    let mut excerpt1_id = None;
 8493    let multibuffer = cx.new_model(|cx| {
 8494        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8495        excerpt1_id = multibuffer
 8496            .push_excerpts(
 8497                buffer.clone(),
 8498                [
 8499                    ExcerptRange {
 8500                        context: Point::new(0, 0)..Point::new(1, 4),
 8501                        primary: None,
 8502                    },
 8503                    ExcerptRange {
 8504                        context: Point::new(1, 0)..Point::new(2, 4),
 8505                        primary: None,
 8506                    },
 8507                ],
 8508                cx,
 8509            )
 8510            .into_iter()
 8511            .next();
 8512        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8513        multibuffer
 8514    });
 8515
 8516    let editor = cx.add_window(|cx| {
 8517        let mut editor = build_editor(multibuffer.clone(), cx);
 8518        let snapshot = editor.snapshot(cx);
 8519        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8520        assert_eq!(
 8521            editor.selections.ranges(cx),
 8522            [Point::new(1, 3)..Point::new(1, 3)]
 8523        );
 8524        editor
 8525    });
 8526
 8527    _ = multibuffer.update(cx, |multibuffer, cx| {
 8528        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8529    });
 8530    _ = editor.update(cx, |editor, cx| {
 8531        assert_eq!(
 8532            editor.selections.ranges(cx),
 8533            [Point::new(0, 0)..Point::new(0, 0)]
 8534        );
 8535
 8536        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8537        editor.change_selections(None, cx, |s| s.refresh());
 8538        assert_eq!(
 8539            editor.selections.ranges(cx),
 8540            [Point::new(0, 3)..Point::new(0, 3)]
 8541        );
 8542        assert!(editor.selections.pending_anchor().is_some());
 8543    });
 8544}
 8545
 8546#[gpui::test]
 8547async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8548    init_test(cx, |_| {});
 8549
 8550    let language = Arc::new(
 8551        Language::new(
 8552            LanguageConfig {
 8553                brackets: BracketPairConfig {
 8554                    pairs: vec![
 8555                        BracketPair {
 8556                            start: "{".to_string(),
 8557                            end: "}".to_string(),
 8558                            close: true,
 8559                            surround: true,
 8560                            newline: true,
 8561                        },
 8562                        BracketPair {
 8563                            start: "/* ".to_string(),
 8564                            end: " */".to_string(),
 8565                            close: true,
 8566                            surround: true,
 8567                            newline: true,
 8568                        },
 8569                    ],
 8570                    ..Default::default()
 8571                },
 8572                ..Default::default()
 8573            },
 8574            Some(tree_sitter_rust::language()),
 8575        )
 8576        .with_indents_query("")
 8577        .unwrap(),
 8578    );
 8579
 8580    let text = concat!(
 8581        "{   }\n",     //
 8582        "  x\n",       //
 8583        "  /*   */\n", //
 8584        "x\n",         //
 8585        "{{} }\n",     //
 8586    );
 8587
 8588    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8589    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8590    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8591    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8592        .await;
 8593
 8594    _ = view.update(cx, |view, cx| {
 8595        view.change_selections(None, cx, |s| {
 8596            s.select_display_ranges([
 8597                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8598                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8599                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8600            ])
 8601        });
 8602        view.newline(&Newline, cx);
 8603
 8604        assert_eq!(
 8605            view.buffer().read(cx).read(cx).text(),
 8606            concat!(
 8607                "{ \n",    // Suppress rustfmt
 8608                "\n",      //
 8609                "}\n",     //
 8610                "  x\n",   //
 8611                "  /* \n", //
 8612                "  \n",    //
 8613                "  */\n",  //
 8614                "x\n",     //
 8615                "{{} \n",  //
 8616                "}\n",     //
 8617            )
 8618        );
 8619    });
 8620}
 8621
 8622#[gpui::test]
 8623fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8624    init_test(cx, |_| {});
 8625
 8626    let editor = cx.add_window(|cx| {
 8627        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8628        build_editor(buffer.clone(), cx)
 8629    });
 8630
 8631    _ = editor.update(cx, |editor, cx| {
 8632        struct Type1;
 8633        struct Type2;
 8634
 8635        let buffer = editor.buffer.read(cx).snapshot(cx);
 8636
 8637        let anchor_range =
 8638            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8639
 8640        editor.highlight_background::<Type1>(
 8641            &[
 8642                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8643                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8644                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8645                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8646            ],
 8647            |_| Hsla::red(),
 8648            cx,
 8649        );
 8650        editor.highlight_background::<Type2>(
 8651            &[
 8652                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8653                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8654                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8655                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8656            ],
 8657            |_| Hsla::green(),
 8658            cx,
 8659        );
 8660
 8661        let snapshot = editor.snapshot(cx);
 8662        let mut highlighted_ranges = editor.background_highlights_in_range(
 8663            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8664            &snapshot,
 8665            cx.theme().colors(),
 8666        );
 8667        // Enforce a consistent ordering based on color without relying on the ordering of the
 8668        // highlight's `TypeId` which is non-executor.
 8669        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8670        assert_eq!(
 8671            highlighted_ranges,
 8672            &[
 8673                (
 8674                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8675                    Hsla::red(),
 8676                ),
 8677                (
 8678                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8679                    Hsla::red(),
 8680                ),
 8681                (
 8682                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8683                    Hsla::green(),
 8684                ),
 8685                (
 8686                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8687                    Hsla::green(),
 8688                ),
 8689            ]
 8690        );
 8691        assert_eq!(
 8692            editor.background_highlights_in_range(
 8693                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8694                &snapshot,
 8695                cx.theme().colors(),
 8696            ),
 8697            &[(
 8698                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8699                Hsla::red(),
 8700            )]
 8701        );
 8702    });
 8703}
 8704
 8705#[gpui::test]
 8706async fn test_following(cx: &mut gpui::TestAppContext) {
 8707    init_test(cx, |_| {});
 8708
 8709    let fs = FakeFs::new(cx.executor());
 8710    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8711
 8712    let buffer = project.update(cx, |project, cx| {
 8713        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8714        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8715    });
 8716    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8717    let follower = cx.update(|cx| {
 8718        cx.open_window(
 8719            WindowOptions {
 8720                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8721                    gpui::Point::new(px(0.), px(0.)),
 8722                    gpui::Point::new(px(10.), px(80.)),
 8723                ))),
 8724                ..Default::default()
 8725            },
 8726            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8727        )
 8728        .unwrap()
 8729    });
 8730
 8731    let is_still_following = Rc::new(RefCell::new(true));
 8732    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8733    let pending_update = Rc::new(RefCell::new(None));
 8734    _ = follower.update(cx, {
 8735        let update = pending_update.clone();
 8736        let is_still_following = is_still_following.clone();
 8737        let follower_edit_event_count = follower_edit_event_count.clone();
 8738        |_, cx| {
 8739            cx.subscribe(
 8740                &leader.root_view(cx).unwrap(),
 8741                move |_, leader, event, cx| {
 8742                    leader
 8743                        .read(cx)
 8744                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8745                },
 8746            )
 8747            .detach();
 8748
 8749            cx.subscribe(
 8750                &follower.root_view(cx).unwrap(),
 8751                move |_, _, event: &EditorEvent, _cx| {
 8752                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8753                        *is_still_following.borrow_mut() = false;
 8754                    }
 8755
 8756                    if let EditorEvent::BufferEdited = event {
 8757                        *follower_edit_event_count.borrow_mut() += 1;
 8758                    }
 8759                },
 8760            )
 8761            .detach();
 8762        }
 8763    });
 8764
 8765    // Update the selections only
 8766    _ = leader.update(cx, |leader, cx| {
 8767        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8768    });
 8769    follower
 8770        .update(cx, |follower, cx| {
 8771            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8772        })
 8773        .unwrap()
 8774        .await
 8775        .unwrap();
 8776    _ = follower.update(cx, |follower, cx| {
 8777        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8778    });
 8779    assert_eq!(*is_still_following.borrow(), true);
 8780    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8781
 8782    // Update the scroll position only
 8783    _ = leader.update(cx, |leader, cx| {
 8784        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8785    });
 8786    follower
 8787        .update(cx, |follower, cx| {
 8788            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8789        })
 8790        .unwrap()
 8791        .await
 8792        .unwrap();
 8793    assert_eq!(
 8794        follower
 8795            .update(cx, |follower, cx| follower.scroll_position(cx))
 8796            .unwrap(),
 8797        gpui::Point::new(1.5, 3.5)
 8798    );
 8799    assert_eq!(*is_still_following.borrow(), true);
 8800    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8801
 8802    // Update the selections and scroll position. The follower's scroll position is updated
 8803    // via autoscroll, not via the leader's exact scroll position.
 8804    _ = leader.update(cx, |leader, cx| {
 8805        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8806        leader.request_autoscroll(Autoscroll::newest(), cx);
 8807        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8808    });
 8809    follower
 8810        .update(cx, |follower, cx| {
 8811            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8812        })
 8813        .unwrap()
 8814        .await
 8815        .unwrap();
 8816    _ = follower.update(cx, |follower, cx| {
 8817        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8818        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8819    });
 8820    assert_eq!(*is_still_following.borrow(), true);
 8821
 8822    // Creating a pending selection that precedes another selection
 8823    _ = leader.update(cx, |leader, cx| {
 8824        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8825        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8826    });
 8827    follower
 8828        .update(cx, |follower, cx| {
 8829            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8830        })
 8831        .unwrap()
 8832        .await
 8833        .unwrap();
 8834    _ = follower.update(cx, |follower, cx| {
 8835        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8836    });
 8837    assert_eq!(*is_still_following.borrow(), true);
 8838
 8839    // Extend the pending selection so that it surrounds another selection
 8840    _ = leader.update(cx, |leader, cx| {
 8841        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8842    });
 8843    follower
 8844        .update(cx, |follower, cx| {
 8845            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8846        })
 8847        .unwrap()
 8848        .await
 8849        .unwrap();
 8850    _ = follower.update(cx, |follower, cx| {
 8851        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8852    });
 8853
 8854    // Scrolling locally breaks the follow
 8855    _ = follower.update(cx, |follower, cx| {
 8856        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8857        follower.set_scroll_anchor(
 8858            ScrollAnchor {
 8859                anchor: top_anchor,
 8860                offset: gpui::Point::new(0.0, 0.5),
 8861            },
 8862            cx,
 8863        );
 8864    });
 8865    assert_eq!(*is_still_following.borrow(), false);
 8866}
 8867
 8868#[gpui::test]
 8869async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8870    init_test(cx, |_| {});
 8871
 8872    let fs = FakeFs::new(cx.executor());
 8873    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8874    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8875    let pane = workspace
 8876        .update(cx, |workspace, _| workspace.active_pane().clone())
 8877        .unwrap();
 8878
 8879    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8880
 8881    let leader = pane.update(cx, |_, cx| {
 8882        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8883        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8884    });
 8885
 8886    // Start following the editor when it has no excerpts.
 8887    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8888    let follower_1 = cx
 8889        .update_window(*workspace.deref(), |_, cx| {
 8890            Editor::from_state_proto(
 8891                workspace.root_view(cx).unwrap(),
 8892                ViewId {
 8893                    creator: Default::default(),
 8894                    id: 0,
 8895                },
 8896                &mut state_message,
 8897                cx,
 8898            )
 8899        })
 8900        .unwrap()
 8901        .unwrap()
 8902        .await
 8903        .unwrap();
 8904
 8905    let update_message = Rc::new(RefCell::new(None));
 8906    follower_1.update(cx, {
 8907        let update = update_message.clone();
 8908        |_, cx| {
 8909            cx.subscribe(&leader, move |_, leader, event, cx| {
 8910                leader
 8911                    .read(cx)
 8912                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8913            })
 8914            .detach();
 8915        }
 8916    });
 8917
 8918    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8919        (
 8920            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8921            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8922        )
 8923    });
 8924
 8925    // Insert some excerpts.
 8926    _ = leader.update(cx, |leader, cx| {
 8927        leader.buffer.update(cx, |multibuffer, cx| {
 8928            let excerpt_ids = multibuffer.push_excerpts(
 8929                buffer_1.clone(),
 8930                [
 8931                    ExcerptRange {
 8932                        context: 1..6,
 8933                        primary: None,
 8934                    },
 8935                    ExcerptRange {
 8936                        context: 12..15,
 8937                        primary: None,
 8938                    },
 8939                    ExcerptRange {
 8940                        context: 0..3,
 8941                        primary: None,
 8942                    },
 8943                ],
 8944                cx,
 8945            );
 8946            multibuffer.insert_excerpts_after(
 8947                excerpt_ids[0],
 8948                buffer_2.clone(),
 8949                [
 8950                    ExcerptRange {
 8951                        context: 8..12,
 8952                        primary: None,
 8953                    },
 8954                    ExcerptRange {
 8955                        context: 0..6,
 8956                        primary: None,
 8957                    },
 8958                ],
 8959                cx,
 8960            );
 8961        });
 8962    });
 8963
 8964    // Apply the update of adding the excerpts.
 8965    follower_1
 8966        .update(cx, |follower, cx| {
 8967            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8968        })
 8969        .await
 8970        .unwrap();
 8971    assert_eq!(
 8972        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8973        leader.update(cx, |editor, cx| editor.text(cx))
 8974    );
 8975    update_message.borrow_mut().take();
 8976
 8977    // Start following separately after it already has excerpts.
 8978    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8979    let follower_2 = cx
 8980        .update_window(*workspace.deref(), |_, cx| {
 8981            Editor::from_state_proto(
 8982                workspace.root_view(cx).unwrap().clone(),
 8983                ViewId {
 8984                    creator: Default::default(),
 8985                    id: 0,
 8986                },
 8987                &mut state_message,
 8988                cx,
 8989            )
 8990        })
 8991        .unwrap()
 8992        .unwrap()
 8993        .await
 8994        .unwrap();
 8995    assert_eq!(
 8996        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8997        leader.update(cx, |editor, cx| editor.text(cx))
 8998    );
 8999
 9000    // Remove some excerpts.
 9001    _ = leader.update(cx, |leader, cx| {
 9002        leader.buffer.update(cx, |multibuffer, cx| {
 9003            let excerpt_ids = multibuffer.excerpt_ids();
 9004            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9005            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9006        });
 9007    });
 9008
 9009    // Apply the update of removing the excerpts.
 9010    follower_1
 9011        .update(cx, |follower, cx| {
 9012            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9013        })
 9014        .await
 9015        .unwrap();
 9016    follower_2
 9017        .update(cx, |follower, cx| {
 9018            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9019        })
 9020        .await
 9021        .unwrap();
 9022    update_message.borrow_mut().take();
 9023    assert_eq!(
 9024        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9025        leader.update(cx, |editor, cx| editor.text(cx))
 9026    );
 9027}
 9028
 9029#[gpui::test]
 9030async fn go_to_prev_overlapping_diagnostic(
 9031    executor: BackgroundExecutor,
 9032    cx: &mut gpui::TestAppContext,
 9033) {
 9034    init_test(cx, |_| {});
 9035
 9036    let mut cx = EditorTestContext::new(cx).await;
 9037    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9038
 9039    cx.set_state(indoc! {"
 9040        ˇfn func(abc def: i32) -> u32 {
 9041        }
 9042    "});
 9043
 9044    _ = cx.update(|cx| {
 9045        _ = project.update(cx, |project, cx| {
 9046            project
 9047                .update_diagnostics(
 9048                    LanguageServerId(0),
 9049                    lsp::PublishDiagnosticsParams {
 9050                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9051                        version: None,
 9052                        diagnostics: vec![
 9053                            lsp::Diagnostic {
 9054                                range: lsp::Range::new(
 9055                                    lsp::Position::new(0, 11),
 9056                                    lsp::Position::new(0, 12),
 9057                                ),
 9058                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9059                                ..Default::default()
 9060                            },
 9061                            lsp::Diagnostic {
 9062                                range: lsp::Range::new(
 9063                                    lsp::Position::new(0, 12),
 9064                                    lsp::Position::new(0, 15),
 9065                                ),
 9066                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9067                                ..Default::default()
 9068                            },
 9069                            lsp::Diagnostic {
 9070                                range: lsp::Range::new(
 9071                                    lsp::Position::new(0, 25),
 9072                                    lsp::Position::new(0, 28),
 9073                                ),
 9074                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9075                                ..Default::default()
 9076                            },
 9077                        ],
 9078                    },
 9079                    &[],
 9080                    cx,
 9081                )
 9082                .unwrap()
 9083        });
 9084    });
 9085
 9086    executor.run_until_parked();
 9087
 9088    cx.update_editor(|editor, cx| {
 9089        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9090    });
 9091
 9092    cx.assert_editor_state(indoc! {"
 9093        fn func(abc def: i32) -> ˇu32 {
 9094        }
 9095    "});
 9096
 9097    cx.update_editor(|editor, cx| {
 9098        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9099    });
 9100
 9101    cx.assert_editor_state(indoc! {"
 9102        fn func(abc ˇdef: i32) -> u32 {
 9103        }
 9104    "});
 9105
 9106    cx.update_editor(|editor, cx| {
 9107        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9108    });
 9109
 9110    cx.assert_editor_state(indoc! {"
 9111        fn func(abcˇ def: i32) -> u32 {
 9112        }
 9113    "});
 9114
 9115    cx.update_editor(|editor, cx| {
 9116        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9117    });
 9118
 9119    cx.assert_editor_state(indoc! {"
 9120        fn func(abc def: i32) -> ˇu32 {
 9121        }
 9122    "});
 9123}
 9124
 9125#[gpui::test]
 9126async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9127    init_test(cx, |_| {});
 9128
 9129    let mut cx = EditorTestContext::new(cx).await;
 9130
 9131    cx.set_state(indoc! {"
 9132        fn func(abˇc def: i32) -> u32 {
 9133        }
 9134    "});
 9135    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9136
 9137    cx.update(|cx| {
 9138        project.update(cx, |project, cx| {
 9139            project.update_diagnostics(
 9140                LanguageServerId(0),
 9141                lsp::PublishDiagnosticsParams {
 9142                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9143                    version: None,
 9144                    diagnostics: vec![lsp::Diagnostic {
 9145                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9146                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9147                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9148                        ..Default::default()
 9149                    }],
 9150                },
 9151                &[],
 9152                cx,
 9153            )
 9154        })
 9155    }).unwrap();
 9156    cx.run_until_parked();
 9157    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9158    cx.run_until_parked();
 9159    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9160}
 9161
 9162#[gpui::test]
 9163async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9164    init_test(cx, |_| {});
 9165
 9166    let mut cx = EditorTestContext::new(cx).await;
 9167
 9168    let diff_base = r#"
 9169        use some::mod;
 9170
 9171        const A: u32 = 42;
 9172
 9173        fn main() {
 9174            println!("hello");
 9175
 9176            println!("world");
 9177        }
 9178        "#
 9179    .unindent();
 9180
 9181    // Edits are modified, removed, modified, added
 9182    cx.set_state(
 9183        &r#"
 9184        use some::modified;
 9185
 9186        ˇ
 9187        fn main() {
 9188            println!("hello there");
 9189
 9190            println!("around the");
 9191            println!("world");
 9192        }
 9193        "#
 9194        .unindent(),
 9195    );
 9196
 9197    cx.set_diff_base(Some(&diff_base));
 9198    executor.run_until_parked();
 9199
 9200    cx.update_editor(|editor, cx| {
 9201        //Wrap around the bottom of the buffer
 9202        for _ in 0..3 {
 9203            editor.go_to_hunk(&GoToHunk, cx);
 9204        }
 9205    });
 9206
 9207    cx.assert_editor_state(
 9208        &r#"
 9209        ˇuse some::modified;
 9210
 9211
 9212        fn main() {
 9213            println!("hello there");
 9214
 9215            println!("around the");
 9216            println!("world");
 9217        }
 9218        "#
 9219        .unindent(),
 9220    );
 9221
 9222    cx.update_editor(|editor, cx| {
 9223        //Wrap around the top of the buffer
 9224        for _ in 0..2 {
 9225            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9226        }
 9227    });
 9228
 9229    cx.assert_editor_state(
 9230        &r#"
 9231        use some::modified;
 9232
 9233
 9234        fn main() {
 9235        ˇ    println!("hello there");
 9236
 9237            println!("around the");
 9238            println!("world");
 9239        }
 9240        "#
 9241        .unindent(),
 9242    );
 9243
 9244    cx.update_editor(|editor, cx| {
 9245        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9246    });
 9247
 9248    cx.assert_editor_state(
 9249        &r#"
 9250        use some::modified;
 9251
 9252        ˇ
 9253        fn main() {
 9254            println!("hello there");
 9255
 9256            println!("around the");
 9257            println!("world");
 9258        }
 9259        "#
 9260        .unindent(),
 9261    );
 9262
 9263    cx.update_editor(|editor, cx| {
 9264        for _ in 0..3 {
 9265            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9266        }
 9267    });
 9268
 9269    cx.assert_editor_state(
 9270        &r#"
 9271        use some::modified;
 9272
 9273
 9274        fn main() {
 9275        ˇ    println!("hello there");
 9276
 9277            println!("around the");
 9278            println!("world");
 9279        }
 9280        "#
 9281        .unindent(),
 9282    );
 9283
 9284    cx.update_editor(|editor, cx| {
 9285        editor.fold(&Fold, cx);
 9286
 9287        //Make sure that the fold only gets one hunk
 9288        for _ in 0..4 {
 9289            editor.go_to_hunk(&GoToHunk, cx);
 9290        }
 9291    });
 9292
 9293    cx.assert_editor_state(
 9294        &r#"
 9295        ˇuse some::modified;
 9296
 9297
 9298        fn main() {
 9299            println!("hello there");
 9300
 9301            println!("around the");
 9302            println!("world");
 9303        }
 9304        "#
 9305        .unindent(),
 9306    );
 9307}
 9308
 9309#[test]
 9310fn test_split_words() {
 9311    fn split(text: &str) -> Vec<&str> {
 9312        split_words(text).collect()
 9313    }
 9314
 9315    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9316    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9317    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9318    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9319    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9320    assert_eq!(split("helloworld"), &["helloworld"]);
 9321
 9322    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9323}
 9324
 9325#[gpui::test]
 9326async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9327    init_test(cx, |_| {});
 9328
 9329    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9330    let mut assert = |before, after| {
 9331        let _state_context = cx.set_state(before);
 9332        cx.update_editor(|editor, cx| {
 9333            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9334        });
 9335        cx.assert_editor_state(after);
 9336    };
 9337
 9338    // Outside bracket jumps to outside of matching bracket
 9339    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9340    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9341
 9342    // Inside bracket jumps to inside of matching bracket
 9343    assert("console.log(ˇvar);", "console.log(varˇ);");
 9344    assert("console.log(varˇ);", "console.log(ˇvar);");
 9345
 9346    // When outside a bracket and inside, favor jumping to the inside bracket
 9347    assert(
 9348        "console.log('foo', [1, 2, 3]ˇ);",
 9349        "console.log(ˇ'foo', [1, 2, 3]);",
 9350    );
 9351    assert(
 9352        "console.log(ˇ'foo', [1, 2, 3]);",
 9353        "console.log('foo', [1, 2, 3]ˇ);",
 9354    );
 9355
 9356    // Bias forward if two options are equally likely
 9357    assert(
 9358        "let result = curried_fun()ˇ();",
 9359        "let result = curried_fun()()ˇ;",
 9360    );
 9361
 9362    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9363    assert(
 9364        indoc! {"
 9365            function test() {
 9366                console.log('test')ˇ
 9367            }"},
 9368        indoc! {"
 9369            function test() {
 9370                console.logˇ('test')
 9371            }"},
 9372    );
 9373}
 9374
 9375#[gpui::test]
 9376async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9377    init_test(cx, |_| {});
 9378
 9379    let fs = FakeFs::new(cx.executor());
 9380    fs.insert_tree(
 9381        "/a",
 9382        json!({
 9383            "main.rs": "fn main() { let a = 5; }",
 9384            "other.rs": "// Test file",
 9385        }),
 9386    )
 9387    .await;
 9388    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9389
 9390    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9391    language_registry.add(Arc::new(Language::new(
 9392        LanguageConfig {
 9393            name: "Rust".into(),
 9394            matcher: LanguageMatcher {
 9395                path_suffixes: vec!["rs".to_string()],
 9396                ..Default::default()
 9397            },
 9398            brackets: BracketPairConfig {
 9399                pairs: vec![BracketPair {
 9400                    start: "{".to_string(),
 9401                    end: "}".to_string(),
 9402                    close: true,
 9403                    surround: true,
 9404                    newline: true,
 9405                }],
 9406                disabled_scopes_by_bracket_ix: Vec::new(),
 9407            },
 9408            ..Default::default()
 9409        },
 9410        Some(tree_sitter_rust::language()),
 9411    )));
 9412    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9413        "Rust",
 9414        FakeLspAdapter {
 9415            capabilities: lsp::ServerCapabilities {
 9416                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9417                    first_trigger_character: "{".to_string(),
 9418                    more_trigger_character: None,
 9419                }),
 9420                ..Default::default()
 9421            },
 9422            ..Default::default()
 9423        },
 9424    );
 9425
 9426    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9427
 9428    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9429
 9430    let worktree_id = workspace
 9431        .update(cx, |workspace, cx| {
 9432            workspace.project().update(cx, |project, cx| {
 9433                project.worktrees(cx).next().unwrap().read(cx).id()
 9434            })
 9435        })
 9436        .unwrap();
 9437
 9438    let buffer = project
 9439        .update(cx, |project, cx| {
 9440            project.open_local_buffer("/a/main.rs", cx)
 9441        })
 9442        .await
 9443        .unwrap();
 9444    cx.executor().run_until_parked();
 9445    cx.executor().start_waiting();
 9446    let fake_server = fake_servers.next().await.unwrap();
 9447    let editor_handle = workspace
 9448        .update(cx, |workspace, cx| {
 9449            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9450        })
 9451        .unwrap()
 9452        .await
 9453        .unwrap()
 9454        .downcast::<Editor>()
 9455        .unwrap();
 9456
 9457    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9458        assert_eq!(
 9459            params.text_document_position.text_document.uri,
 9460            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9461        );
 9462        assert_eq!(
 9463            params.text_document_position.position,
 9464            lsp::Position::new(0, 21),
 9465        );
 9466
 9467        Ok(Some(vec![lsp::TextEdit {
 9468            new_text: "]".to_string(),
 9469            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9470        }]))
 9471    });
 9472
 9473    editor_handle.update(cx, |editor, cx| {
 9474        editor.focus(cx);
 9475        editor.change_selections(None, cx, |s| {
 9476            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9477        });
 9478        editor.handle_input("{", cx);
 9479    });
 9480
 9481    cx.executor().run_until_parked();
 9482
 9483    _ = buffer.update(cx, |buffer, _| {
 9484        assert_eq!(
 9485            buffer.text(),
 9486            "fn main() { let a = {5}; }",
 9487            "No extra braces from on type formatting should appear in the buffer"
 9488        )
 9489    });
 9490}
 9491
 9492#[gpui::test]
 9493async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9494    init_test(cx, |_| {});
 9495
 9496    let fs = FakeFs::new(cx.executor());
 9497    fs.insert_tree(
 9498        "/a",
 9499        json!({
 9500            "main.rs": "fn main() { let a = 5; }",
 9501            "other.rs": "// Test file",
 9502        }),
 9503    )
 9504    .await;
 9505
 9506    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9507
 9508    let server_restarts = Arc::new(AtomicUsize::new(0));
 9509    let closure_restarts = Arc::clone(&server_restarts);
 9510    let language_server_name = "test language server";
 9511    let language_name: Arc<str> = "Rust".into();
 9512
 9513    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9514    language_registry.add(Arc::new(Language::new(
 9515        LanguageConfig {
 9516            name: Arc::clone(&language_name),
 9517            matcher: LanguageMatcher {
 9518                path_suffixes: vec!["rs".to_string()],
 9519                ..Default::default()
 9520            },
 9521            ..Default::default()
 9522        },
 9523        Some(tree_sitter_rust::language()),
 9524    )));
 9525    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9526        "Rust",
 9527        FakeLspAdapter {
 9528            name: language_server_name,
 9529            initialization_options: Some(json!({
 9530                "testOptionValue": true
 9531            })),
 9532            initializer: Some(Box::new(move |fake_server| {
 9533                let task_restarts = Arc::clone(&closure_restarts);
 9534                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9535                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9536                    futures::future::ready(Ok(()))
 9537                });
 9538            })),
 9539            ..Default::default()
 9540        },
 9541    );
 9542
 9543    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9544    let _buffer = project
 9545        .update(cx, |project, cx| {
 9546            project.open_local_buffer("/a/main.rs", cx)
 9547        })
 9548        .await
 9549        .unwrap();
 9550    let _fake_server = fake_servers.next().await.unwrap();
 9551    update_test_language_settings(cx, |language_settings| {
 9552        language_settings.languages.insert(
 9553            Arc::clone(&language_name),
 9554            LanguageSettingsContent {
 9555                tab_size: NonZeroU32::new(8),
 9556                ..Default::default()
 9557            },
 9558        );
 9559    });
 9560    cx.executor().run_until_parked();
 9561    assert_eq!(
 9562        server_restarts.load(atomic::Ordering::Acquire),
 9563        0,
 9564        "Should not restart LSP server on an unrelated change"
 9565    );
 9566
 9567    update_test_project_settings(cx, |project_settings| {
 9568        project_settings.lsp.insert(
 9569            "Some other server name".into(),
 9570            LspSettings {
 9571                binary: None,
 9572                settings: None,
 9573                initialization_options: Some(json!({
 9574                    "some other init value": false
 9575                })),
 9576            },
 9577        );
 9578    });
 9579    cx.executor().run_until_parked();
 9580    assert_eq!(
 9581        server_restarts.load(atomic::Ordering::Acquire),
 9582        0,
 9583        "Should not restart LSP server on an unrelated LSP settings change"
 9584    );
 9585
 9586    update_test_project_settings(cx, |project_settings| {
 9587        project_settings.lsp.insert(
 9588            language_server_name.into(),
 9589            LspSettings {
 9590                binary: None,
 9591                settings: None,
 9592                initialization_options: Some(json!({
 9593                    "anotherInitValue": false
 9594                })),
 9595            },
 9596        );
 9597    });
 9598    cx.executor().run_until_parked();
 9599    assert_eq!(
 9600        server_restarts.load(atomic::Ordering::Acquire),
 9601        1,
 9602        "Should restart LSP server on a related LSP settings change"
 9603    );
 9604
 9605    update_test_project_settings(cx, |project_settings| {
 9606        project_settings.lsp.insert(
 9607            language_server_name.into(),
 9608            LspSettings {
 9609                binary: None,
 9610                settings: None,
 9611                initialization_options: Some(json!({
 9612                    "anotherInitValue": false
 9613                })),
 9614            },
 9615        );
 9616    });
 9617    cx.executor().run_until_parked();
 9618    assert_eq!(
 9619        server_restarts.load(atomic::Ordering::Acquire),
 9620        1,
 9621        "Should not restart LSP server on a related LSP settings change that is the same"
 9622    );
 9623
 9624    update_test_project_settings(cx, |project_settings| {
 9625        project_settings.lsp.insert(
 9626            language_server_name.into(),
 9627            LspSettings {
 9628                binary: None,
 9629                settings: None,
 9630                initialization_options: None,
 9631            },
 9632        );
 9633    });
 9634    cx.executor().run_until_parked();
 9635    assert_eq!(
 9636        server_restarts.load(atomic::Ordering::Acquire),
 9637        2,
 9638        "Should restart LSP server on another related LSP settings change"
 9639    );
 9640}
 9641
 9642#[gpui::test]
 9643async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9644    init_test(cx, |_| {});
 9645
 9646    let mut cx = EditorLspTestContext::new_rust(
 9647        lsp::ServerCapabilities {
 9648            completion_provider: Some(lsp::CompletionOptions {
 9649                trigger_characters: Some(vec![".".to_string()]),
 9650                resolve_provider: Some(true),
 9651                ..Default::default()
 9652            }),
 9653            ..Default::default()
 9654        },
 9655        cx,
 9656    )
 9657    .await;
 9658
 9659    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9660    cx.simulate_keystroke(".");
 9661    let completion_item = lsp::CompletionItem {
 9662        label: "some".into(),
 9663        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9664        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9665        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9666            kind: lsp::MarkupKind::Markdown,
 9667            value: "```rust\nSome(2)\n```".to_string(),
 9668        })),
 9669        deprecated: Some(false),
 9670        sort_text: Some("fffffff2".to_string()),
 9671        filter_text: Some("some".to_string()),
 9672        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9673        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9674            range: lsp::Range {
 9675                start: lsp::Position {
 9676                    line: 0,
 9677                    character: 22,
 9678                },
 9679                end: lsp::Position {
 9680                    line: 0,
 9681                    character: 22,
 9682                },
 9683            },
 9684            new_text: "Some(2)".to_string(),
 9685        })),
 9686        additional_text_edits: Some(vec![lsp::TextEdit {
 9687            range: lsp::Range {
 9688                start: lsp::Position {
 9689                    line: 0,
 9690                    character: 20,
 9691                },
 9692                end: lsp::Position {
 9693                    line: 0,
 9694                    character: 22,
 9695                },
 9696            },
 9697            new_text: "".to_string(),
 9698        }]),
 9699        ..Default::default()
 9700    };
 9701
 9702    let closure_completion_item = completion_item.clone();
 9703    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9704        let task_completion_item = closure_completion_item.clone();
 9705        async move {
 9706            Ok(Some(lsp::CompletionResponse::Array(vec![
 9707                task_completion_item,
 9708            ])))
 9709        }
 9710    });
 9711
 9712    request.next().await;
 9713
 9714    cx.condition(|editor, _| editor.context_menu_visible())
 9715        .await;
 9716    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9717        editor
 9718            .confirm_completion(&ConfirmCompletion::default(), cx)
 9719            .unwrap()
 9720    });
 9721    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9722
 9723    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9724        let task_completion_item = completion_item.clone();
 9725        async move { Ok(task_completion_item) }
 9726    })
 9727    .next()
 9728    .await
 9729    .unwrap();
 9730    apply_additional_edits.await.unwrap();
 9731    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9732}
 9733
 9734#[gpui::test]
 9735async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9736    init_test(cx, |_| {});
 9737
 9738    let mut cx = EditorLspTestContext::new(
 9739        Language::new(
 9740            LanguageConfig {
 9741                matcher: LanguageMatcher {
 9742                    path_suffixes: vec!["jsx".into()],
 9743                    ..Default::default()
 9744                },
 9745                overrides: [(
 9746                    "element".into(),
 9747                    LanguageConfigOverride {
 9748                        word_characters: Override::Set(['-'].into_iter().collect()),
 9749                        ..Default::default()
 9750                    },
 9751                )]
 9752                .into_iter()
 9753                .collect(),
 9754                ..Default::default()
 9755            },
 9756            Some(tree_sitter_typescript::language_tsx()),
 9757        )
 9758        .with_override_query("(jsx_self_closing_element) @element")
 9759        .unwrap(),
 9760        lsp::ServerCapabilities {
 9761            completion_provider: Some(lsp::CompletionOptions {
 9762                trigger_characters: Some(vec![":".to_string()]),
 9763                ..Default::default()
 9764            }),
 9765            ..Default::default()
 9766        },
 9767        cx,
 9768    )
 9769    .await;
 9770
 9771    cx.lsp
 9772        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9773            Ok(Some(lsp::CompletionResponse::Array(vec![
 9774                lsp::CompletionItem {
 9775                    label: "bg-blue".into(),
 9776                    ..Default::default()
 9777                },
 9778                lsp::CompletionItem {
 9779                    label: "bg-red".into(),
 9780                    ..Default::default()
 9781                },
 9782                lsp::CompletionItem {
 9783                    label: "bg-yellow".into(),
 9784                    ..Default::default()
 9785                },
 9786            ])))
 9787        });
 9788
 9789    cx.set_state(r#"<p class="bgˇ" />"#);
 9790
 9791    // Trigger completion when typing a dash, because the dash is an extra
 9792    // word character in the 'element' scope, which contains the cursor.
 9793    cx.simulate_keystroke("-");
 9794    cx.executor().run_until_parked();
 9795    cx.update_editor(|editor, _| {
 9796        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9797            assert_eq!(
 9798                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9799                &["bg-red", "bg-blue", "bg-yellow"]
 9800            );
 9801        } else {
 9802            panic!("expected completion menu to be open");
 9803        }
 9804    });
 9805
 9806    cx.simulate_keystroke("l");
 9807    cx.executor().run_until_parked();
 9808    cx.update_editor(|editor, _| {
 9809        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9810            assert_eq!(
 9811                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9812                &["bg-blue", "bg-yellow"]
 9813            );
 9814        } else {
 9815            panic!("expected completion menu to be open");
 9816        }
 9817    });
 9818
 9819    // When filtering completions, consider the character after the '-' to
 9820    // be the start of a subword.
 9821    cx.set_state(r#"<p class="yelˇ" />"#);
 9822    cx.simulate_keystroke("l");
 9823    cx.executor().run_until_parked();
 9824    cx.update_editor(|editor, _| {
 9825        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9826            assert_eq!(
 9827                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9828                &["bg-yellow"]
 9829            );
 9830        } else {
 9831            panic!("expected completion menu to be open");
 9832        }
 9833    });
 9834}
 9835
 9836#[gpui::test]
 9837async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9838    init_test(cx, |settings| {
 9839        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9840            FormatterList(vec![Formatter::Prettier].into()),
 9841        ))
 9842    });
 9843
 9844    let fs = FakeFs::new(cx.executor());
 9845    fs.insert_file("/file.ts", Default::default()).await;
 9846
 9847    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9848    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9849
 9850    language_registry.add(Arc::new(Language::new(
 9851        LanguageConfig {
 9852            name: "TypeScript".into(),
 9853            matcher: LanguageMatcher {
 9854                path_suffixes: vec!["ts".to_string()],
 9855                ..Default::default()
 9856            },
 9857            ..Default::default()
 9858        },
 9859        Some(tree_sitter_rust::language()),
 9860    )));
 9861    update_test_language_settings(cx, |settings| {
 9862        settings.defaults.prettier = Some(PrettierSettings {
 9863            allowed: true,
 9864            ..PrettierSettings::default()
 9865        });
 9866    });
 9867
 9868    let test_plugin = "test_plugin";
 9869    let _ = language_registry.register_fake_lsp_adapter(
 9870        "TypeScript",
 9871        FakeLspAdapter {
 9872            prettier_plugins: vec![test_plugin],
 9873            ..Default::default()
 9874        },
 9875    );
 9876
 9877    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9878    let buffer = project
 9879        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9880        .await
 9881        .unwrap();
 9882
 9883    let buffer_text = "one\ntwo\nthree\n";
 9884    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9885    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9886    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9887
 9888    editor
 9889        .update(cx, |editor, cx| {
 9890            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9891        })
 9892        .unwrap()
 9893        .await;
 9894    assert_eq!(
 9895        editor.update(cx, |editor, cx| editor.text(cx)),
 9896        buffer_text.to_string() + prettier_format_suffix,
 9897        "Test prettier formatting was not applied to the original buffer text",
 9898    );
 9899
 9900    update_test_language_settings(cx, |settings| {
 9901        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9902    });
 9903    let format = editor.update(cx, |editor, cx| {
 9904        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9905    });
 9906    format.await.unwrap();
 9907    assert_eq!(
 9908        editor.update(cx, |editor, cx| editor.text(cx)),
 9909        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9910        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9911    );
 9912}
 9913
 9914#[gpui::test]
 9915async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9916    init_test(cx, |_| {});
 9917    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9918    let base_text = indoc! {r#"struct Row;
 9919struct Row1;
 9920struct Row2;
 9921
 9922struct Row4;
 9923struct Row5;
 9924struct Row6;
 9925
 9926struct Row8;
 9927struct Row9;
 9928struct Row10;"#};
 9929
 9930    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9931    assert_hunk_revert(
 9932        indoc! {r#"struct Row;
 9933                   struct Row1;
 9934                   struct Row1.1;
 9935                   struct Row1.2;
 9936                   struct Row2;ˇ
 9937
 9938                   struct Row4;
 9939                   struct Row5;
 9940                   struct Row6;
 9941
 9942                   struct Row8;
 9943                   ˇstruct Row9;
 9944                   struct Row9.1;
 9945                   struct Row9.2;
 9946                   struct Row9.3;
 9947                   struct Row10;"#},
 9948        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9949        indoc! {r#"struct Row;
 9950                   struct Row1;
 9951                   struct Row1.1;
 9952                   struct Row1.2;
 9953                   struct Row2;ˇ
 9954
 9955                   struct Row4;
 9956                   struct Row5;
 9957                   struct Row6;
 9958
 9959                   struct Row8;
 9960                   ˇstruct Row9;
 9961                   struct Row9.1;
 9962                   struct Row9.2;
 9963                   struct Row9.3;
 9964                   struct Row10;"#},
 9965        base_text,
 9966        &mut cx,
 9967    );
 9968    // Same for selections
 9969    assert_hunk_revert(
 9970        indoc! {r#"struct Row;
 9971                   struct Row1;
 9972                   struct Row2;
 9973                   struct Row2.1;
 9974                   struct Row2.2;
 9975                   «ˇ
 9976                   struct Row4;
 9977                   struct» Row5;
 9978                   «struct Row6;
 9979                   ˇ»
 9980                   struct Row9.1;
 9981                   struct Row9.2;
 9982                   struct Row9.3;
 9983                   struct Row8;
 9984                   struct Row9;
 9985                   struct Row10;"#},
 9986        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9987        indoc! {r#"struct Row;
 9988                   struct Row1;
 9989                   struct Row2;
 9990                   struct Row2.1;
 9991                   struct Row2.2;
 9992                   «ˇ
 9993                   struct Row4;
 9994                   struct» Row5;
 9995                   «struct Row6;
 9996                   ˇ»
 9997                   struct Row9.1;
 9998                   struct Row9.2;
 9999                   struct Row9.3;
10000                   struct Row8;
10001                   struct Row9;
10002                   struct Row10;"#},
10003        base_text,
10004        &mut cx,
10005    );
10006
10007    // When carets and selections intersect the addition hunks, those are reverted.
10008    // Adjacent carets got merged.
10009    assert_hunk_revert(
10010        indoc! {r#"struct Row;
10011                   ˇ// something on the top
10012                   struct Row1;
10013                   struct Row2;
10014                   struct Roˇw3.1;
10015                   struct Row2.2;
10016                   struct Row2.3;ˇ
10017
10018                   struct Row4;
10019                   struct ˇRow5.1;
10020                   struct Row5.2;
10021                   struct «Rowˇ»5.3;
10022                   struct Row5;
10023                   struct Row6;
10024                   ˇ
10025                   struct Row9.1;
10026                   struct «Rowˇ»9.2;
10027                   struct «ˇRow»9.3;
10028                   struct Row8;
10029                   struct Row9;
10030                   «ˇ// something on bottom»
10031                   struct Row10;"#},
10032        vec![
10033            DiffHunkStatus::Added,
10034            DiffHunkStatus::Added,
10035            DiffHunkStatus::Added,
10036            DiffHunkStatus::Added,
10037            DiffHunkStatus::Added,
10038        ],
10039        indoc! {r#"struct Row;
10040                   ˇstruct Row1;
10041                   struct Row2;
10042                   ˇ
10043                   struct Row4;
10044                   ˇstruct Row5;
10045                   struct Row6;
10046                   ˇ
10047                   ˇstruct Row8;
10048                   struct Row9;
10049                   ˇstruct Row10;"#},
10050        base_text,
10051        &mut cx,
10052    );
10053}
10054
10055#[gpui::test]
10056async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10057    init_test(cx, |_| {});
10058    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10059    let base_text = indoc! {r#"struct Row;
10060struct Row1;
10061struct Row2;
10062
10063struct Row4;
10064struct Row5;
10065struct Row6;
10066
10067struct Row8;
10068struct Row9;
10069struct Row10;"#};
10070
10071    // Modification hunks behave the same as the addition ones.
10072    assert_hunk_revert(
10073        indoc! {r#"struct Row;
10074                   struct Row1;
10075                   struct Row33;
10076                   ˇ
10077                   struct Row4;
10078                   struct Row5;
10079                   struct Row6;
10080                   ˇ
10081                   struct Row99;
10082                   struct Row9;
10083                   struct Row10;"#},
10084        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10085        indoc! {r#"struct Row;
10086                   struct Row1;
10087                   struct Row33;
10088                   ˇ
10089                   struct Row4;
10090                   struct Row5;
10091                   struct Row6;
10092                   ˇ
10093                   struct Row99;
10094                   struct Row9;
10095                   struct Row10;"#},
10096        base_text,
10097        &mut cx,
10098    );
10099    assert_hunk_revert(
10100        indoc! {r#"struct Row;
10101                   struct Row1;
10102                   struct Row33;
10103                   «ˇ
10104                   struct Row4;
10105                   struct» Row5;
10106                   «struct Row6;
10107                   ˇ»
10108                   struct Row99;
10109                   struct Row9;
10110                   struct Row10;"#},
10111        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10112        indoc! {r#"struct Row;
10113                   struct Row1;
10114                   struct Row33;
10115                   «ˇ
10116                   struct Row4;
10117                   struct» Row5;
10118                   «struct Row6;
10119                   ˇ»
10120                   struct Row99;
10121                   struct Row9;
10122                   struct Row10;"#},
10123        base_text,
10124        &mut cx,
10125    );
10126
10127    assert_hunk_revert(
10128        indoc! {r#"ˇstruct Row1.1;
10129                   struct Row1;
10130                   «ˇstr»uct Row22;
10131
10132                   struct ˇRow44;
10133                   struct Row5;
10134                   struct «Rˇ»ow66;ˇ
10135
10136                   «struˇ»ct Row88;
10137                   struct Row9;
10138                   struct Row1011;ˇ"#},
10139        vec![
10140            DiffHunkStatus::Modified,
10141            DiffHunkStatus::Modified,
10142            DiffHunkStatus::Modified,
10143            DiffHunkStatus::Modified,
10144            DiffHunkStatus::Modified,
10145            DiffHunkStatus::Modified,
10146        ],
10147        indoc! {r#"struct Row;
10148                   ˇstruct Row1;
10149                   struct Row2;
10150                   ˇ
10151                   struct Row4;
10152                   ˇstruct Row5;
10153                   struct Row6;
10154                   ˇ
10155                   struct Row8;
10156                   ˇstruct Row9;
10157                   struct Row10;ˇ"#},
10158        base_text,
10159        &mut cx,
10160    );
10161}
10162
10163#[gpui::test]
10164async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10165    init_test(cx, |_| {});
10166    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10167    let base_text = indoc! {r#"struct Row;
10168struct Row1;
10169struct Row2;
10170
10171struct Row4;
10172struct Row5;
10173struct Row6;
10174
10175struct Row8;
10176struct Row9;
10177struct Row10;"#};
10178
10179    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10180    assert_hunk_revert(
10181        indoc! {r#"struct Row;
10182                   struct Row2;
10183
10184                   ˇstruct Row4;
10185                   struct Row5;
10186                   struct Row6;
10187                   ˇ
10188                   struct Row8;
10189                   struct Row10;"#},
10190        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10191        indoc! {r#"struct Row;
10192                   struct Row2;
10193
10194                   ˇstruct Row4;
10195                   struct Row5;
10196                   struct Row6;
10197                   ˇ
10198                   struct Row8;
10199                   struct Row10;"#},
10200        base_text,
10201        &mut cx,
10202    );
10203    assert_hunk_revert(
10204        indoc! {r#"struct Row;
10205                   struct Row2;
10206
10207                   «ˇstruct Row4;
10208                   struct» Row5;
10209                   «struct Row6;
10210                   ˇ»
10211                   struct Row8;
10212                   struct Row10;"#},
10213        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10214        indoc! {r#"struct Row;
10215                   struct Row2;
10216
10217                   «ˇstruct Row4;
10218                   struct» Row5;
10219                   «struct Row6;
10220                   ˇ»
10221                   struct Row8;
10222                   struct Row10;"#},
10223        base_text,
10224        &mut cx,
10225    );
10226
10227    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10228    assert_hunk_revert(
10229        indoc! {r#"struct Row;
10230                   ˇstruct Row2;
10231
10232                   struct Row4;
10233                   struct Row5;
10234                   struct Row6;
10235
10236                   struct Row8;ˇ
10237                   struct Row10;"#},
10238        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10239        indoc! {r#"struct Row;
10240                   struct Row1;
10241                   ˇstruct Row2;
10242
10243                   struct Row4;
10244                   struct Row5;
10245                   struct Row6;
10246
10247                   struct Row8;ˇ
10248                   struct Row9;
10249                   struct Row10;"#},
10250        base_text,
10251        &mut cx,
10252    );
10253    assert_hunk_revert(
10254        indoc! {r#"struct Row;
10255                   struct Row2«ˇ;
10256                   struct Row4;
10257                   struct» Row5;
10258                   «struct Row6;
10259
10260                   struct Row8;ˇ»
10261                   struct Row10;"#},
10262        vec![
10263            DiffHunkStatus::Removed,
10264            DiffHunkStatus::Removed,
10265            DiffHunkStatus::Removed,
10266        ],
10267        indoc! {r#"struct Row;
10268                   struct Row1;
10269                   struct Row2«ˇ;
10270
10271                   struct Row4;
10272                   struct» Row5;
10273                   «struct Row6;
10274
10275                   struct Row8;ˇ»
10276                   struct Row9;
10277                   struct Row10;"#},
10278        base_text,
10279        &mut cx,
10280    );
10281}
10282
10283#[gpui::test]
10284async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10285    init_test(cx, |_| {});
10286
10287    let cols = 4;
10288    let rows = 10;
10289    let sample_text_1 = sample_text(rows, cols, 'a');
10290    assert_eq!(
10291        sample_text_1,
10292        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10293    );
10294    let sample_text_2 = sample_text(rows, cols, 'l');
10295    assert_eq!(
10296        sample_text_2,
10297        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10298    );
10299    let sample_text_3 = sample_text(rows, cols, 'v');
10300    assert_eq!(
10301        sample_text_3,
10302        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10303    );
10304
10305    fn diff_every_buffer_row(
10306        buffer: &Model<Buffer>,
10307        sample_text: String,
10308        cols: usize,
10309        cx: &mut gpui::TestAppContext,
10310    ) {
10311        // revert first character in each row, creating one large diff hunk per buffer
10312        let is_first_char = |offset: usize| offset % cols == 0;
10313        buffer.update(cx, |buffer, cx| {
10314            buffer.set_text(
10315                sample_text
10316                    .chars()
10317                    .enumerate()
10318                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10319                    .collect::<String>(),
10320                cx,
10321            );
10322            buffer.set_diff_base(Some(sample_text), cx);
10323        });
10324        cx.executor().run_until_parked();
10325    }
10326
10327    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10328    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10329
10330    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10331    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10332
10333    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10334    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10335
10336    let multibuffer = cx.new_model(|cx| {
10337        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10338        multibuffer.push_excerpts(
10339            buffer_1.clone(),
10340            [
10341                ExcerptRange {
10342                    context: Point::new(0, 0)..Point::new(3, 0),
10343                    primary: None,
10344                },
10345                ExcerptRange {
10346                    context: Point::new(5, 0)..Point::new(7, 0),
10347                    primary: None,
10348                },
10349                ExcerptRange {
10350                    context: Point::new(9, 0)..Point::new(10, 4),
10351                    primary: None,
10352                },
10353            ],
10354            cx,
10355        );
10356        multibuffer.push_excerpts(
10357            buffer_2.clone(),
10358            [
10359                ExcerptRange {
10360                    context: Point::new(0, 0)..Point::new(3, 0),
10361                    primary: None,
10362                },
10363                ExcerptRange {
10364                    context: Point::new(5, 0)..Point::new(7, 0),
10365                    primary: None,
10366                },
10367                ExcerptRange {
10368                    context: Point::new(9, 0)..Point::new(10, 4),
10369                    primary: None,
10370                },
10371            ],
10372            cx,
10373        );
10374        multibuffer.push_excerpts(
10375            buffer_3.clone(),
10376            [
10377                ExcerptRange {
10378                    context: Point::new(0, 0)..Point::new(3, 0),
10379                    primary: None,
10380                },
10381                ExcerptRange {
10382                    context: Point::new(5, 0)..Point::new(7, 0),
10383                    primary: None,
10384                },
10385                ExcerptRange {
10386                    context: Point::new(9, 0)..Point::new(10, 4),
10387                    primary: None,
10388                },
10389            ],
10390            cx,
10391        );
10392        multibuffer
10393    });
10394
10395    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10396    editor.update(cx, |editor, cx| {
10397        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");
10398        editor.select_all(&SelectAll, cx);
10399        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10400    });
10401    cx.executor().run_until_parked();
10402    // When all ranges are selected, all buffer hunks are reverted.
10403    editor.update(cx, |editor, cx| {
10404        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");
10405    });
10406    buffer_1.update(cx, |buffer, _| {
10407        assert_eq!(buffer.text(), sample_text_1);
10408    });
10409    buffer_2.update(cx, |buffer, _| {
10410        assert_eq!(buffer.text(), sample_text_2);
10411    });
10412    buffer_3.update(cx, |buffer, _| {
10413        assert_eq!(buffer.text(), sample_text_3);
10414    });
10415
10416    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10417    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10418    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10419    editor.update(cx, |editor, cx| {
10420        editor.change_selections(None, cx, |s| {
10421            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10422        });
10423        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10424    });
10425    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10426    // but not affect buffer_2 and its related excerpts.
10427    editor.update(cx, |editor, cx| {
10428        assert_eq!(
10429            editor.text(cx),
10430            "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"
10431        );
10432    });
10433    buffer_1.update(cx, |buffer, _| {
10434        assert_eq!(buffer.text(), sample_text_1);
10435    });
10436    buffer_2.update(cx, |buffer, _| {
10437        assert_eq!(
10438            buffer.text(),
10439            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10440        );
10441    });
10442    buffer_3.update(cx, |buffer, _| {
10443        assert_eq!(
10444            buffer.text(),
10445            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10446        );
10447    });
10448}
10449
10450#[gpui::test]
10451async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10452    init_test(cx, |_| {});
10453
10454    let cols = 4;
10455    let rows = 10;
10456    let sample_text_1 = sample_text(rows, cols, 'a');
10457    assert_eq!(
10458        sample_text_1,
10459        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10460    );
10461    let sample_text_2 = sample_text(rows, cols, 'l');
10462    assert_eq!(
10463        sample_text_2,
10464        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10465    );
10466    let sample_text_3 = sample_text(rows, cols, 'v');
10467    assert_eq!(
10468        sample_text_3,
10469        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10470    );
10471
10472    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10473    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10474    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10475
10476    let multi_buffer = cx.new_model(|cx| {
10477        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10478        multibuffer.push_excerpts(
10479            buffer_1.clone(),
10480            [
10481                ExcerptRange {
10482                    context: Point::new(0, 0)..Point::new(3, 0),
10483                    primary: None,
10484                },
10485                ExcerptRange {
10486                    context: Point::new(5, 0)..Point::new(7, 0),
10487                    primary: None,
10488                },
10489                ExcerptRange {
10490                    context: Point::new(9, 0)..Point::new(10, 4),
10491                    primary: None,
10492                },
10493            ],
10494            cx,
10495        );
10496        multibuffer.push_excerpts(
10497            buffer_2.clone(),
10498            [
10499                ExcerptRange {
10500                    context: Point::new(0, 0)..Point::new(3, 0),
10501                    primary: None,
10502                },
10503                ExcerptRange {
10504                    context: Point::new(5, 0)..Point::new(7, 0),
10505                    primary: None,
10506                },
10507                ExcerptRange {
10508                    context: Point::new(9, 0)..Point::new(10, 4),
10509                    primary: None,
10510                },
10511            ],
10512            cx,
10513        );
10514        multibuffer.push_excerpts(
10515            buffer_3.clone(),
10516            [
10517                ExcerptRange {
10518                    context: Point::new(0, 0)..Point::new(3, 0),
10519                    primary: None,
10520                },
10521                ExcerptRange {
10522                    context: Point::new(5, 0)..Point::new(7, 0),
10523                    primary: None,
10524                },
10525                ExcerptRange {
10526                    context: Point::new(9, 0)..Point::new(10, 4),
10527                    primary: None,
10528                },
10529            ],
10530            cx,
10531        );
10532        multibuffer
10533    });
10534
10535    let fs = FakeFs::new(cx.executor());
10536    fs.insert_tree(
10537        "/a",
10538        json!({
10539            "main.rs": sample_text_1,
10540            "other.rs": sample_text_2,
10541            "lib.rs": sample_text_3,
10542        }),
10543    )
10544    .await;
10545    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10546    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10547    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10548    let multi_buffer_editor = cx.new_view(|cx| {
10549        Editor::new(
10550            EditorMode::Full,
10551            multi_buffer,
10552            Some(project.clone()),
10553            true,
10554            cx,
10555        )
10556    });
10557    let multibuffer_item_id = workspace
10558        .update(cx, |workspace, cx| {
10559            assert!(
10560                workspace.active_item(cx).is_none(),
10561                "active item should be None before the first item is added"
10562            );
10563            workspace.add_item_to_active_pane(
10564                Box::new(multi_buffer_editor.clone()),
10565                None,
10566                true,
10567                cx,
10568            );
10569            let active_item = workspace
10570                .active_item(cx)
10571                .expect("should have an active item after adding the multi buffer");
10572            assert!(
10573                !active_item.is_singleton(cx),
10574                "A multi buffer was expected to active after adding"
10575            );
10576            active_item.item_id()
10577        })
10578        .unwrap();
10579    cx.executor().run_until_parked();
10580
10581    multi_buffer_editor.update(cx, |editor, cx| {
10582        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10583        editor.open_excerpts(&OpenExcerpts, cx);
10584    });
10585    cx.executor().run_until_parked();
10586    let first_item_id = workspace
10587        .update(cx, |workspace, cx| {
10588            let active_item = workspace
10589                .active_item(cx)
10590                .expect("should have an active item after navigating into the 1st buffer");
10591            let first_item_id = active_item.item_id();
10592            assert_ne!(
10593                first_item_id, multibuffer_item_id,
10594                "Should navigate into the 1st buffer and activate it"
10595            );
10596            assert!(
10597                active_item.is_singleton(cx),
10598                "New active item should be a singleton buffer"
10599            );
10600            assert_eq!(
10601                active_item
10602                    .act_as::<Editor>(cx)
10603                    .expect("should have navigated into an editor for the 1st buffer")
10604                    .read(cx)
10605                    .text(cx),
10606                sample_text_1
10607            );
10608
10609            workspace
10610                .go_back(workspace.active_pane().downgrade(), cx)
10611                .detach_and_log_err(cx);
10612
10613            first_item_id
10614        })
10615        .unwrap();
10616    cx.executor().run_until_parked();
10617    workspace
10618        .update(cx, |workspace, cx| {
10619            let active_item = workspace
10620                .active_item(cx)
10621                .expect("should have an active item after navigating back");
10622            assert_eq!(
10623                active_item.item_id(),
10624                multibuffer_item_id,
10625                "Should navigate back to the multi buffer"
10626            );
10627            assert!(!active_item.is_singleton(cx));
10628        })
10629        .unwrap();
10630
10631    multi_buffer_editor.update(cx, |editor, cx| {
10632        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10633            s.select_ranges(Some(39..40))
10634        });
10635        editor.open_excerpts(&OpenExcerpts, cx);
10636    });
10637    cx.executor().run_until_parked();
10638    let second_item_id = workspace
10639        .update(cx, |workspace, cx| {
10640            let active_item = workspace
10641                .active_item(cx)
10642                .expect("should have an active item after navigating into the 2nd buffer");
10643            let second_item_id = active_item.item_id();
10644            assert_ne!(
10645                second_item_id, multibuffer_item_id,
10646                "Should navigate away from the multibuffer"
10647            );
10648            assert_ne!(
10649                second_item_id, first_item_id,
10650                "Should navigate into the 2nd buffer and activate it"
10651            );
10652            assert!(
10653                active_item.is_singleton(cx),
10654                "New active item should be a singleton buffer"
10655            );
10656            assert_eq!(
10657                active_item
10658                    .act_as::<Editor>(cx)
10659                    .expect("should have navigated into an editor")
10660                    .read(cx)
10661                    .text(cx),
10662                sample_text_2
10663            );
10664
10665            workspace
10666                .go_back(workspace.active_pane().downgrade(), cx)
10667                .detach_and_log_err(cx);
10668
10669            second_item_id
10670        })
10671        .unwrap();
10672    cx.executor().run_until_parked();
10673    workspace
10674        .update(cx, |workspace, cx| {
10675            let active_item = workspace
10676                .active_item(cx)
10677                .expect("should have an active item after navigating back from the 2nd buffer");
10678            assert_eq!(
10679                active_item.item_id(),
10680                multibuffer_item_id,
10681                "Should navigate back from the 2nd buffer to the multi buffer"
10682            );
10683            assert!(!active_item.is_singleton(cx));
10684        })
10685        .unwrap();
10686
10687    multi_buffer_editor.update(cx, |editor, cx| {
10688        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10689            s.select_ranges(Some(60..70))
10690        });
10691        editor.open_excerpts(&OpenExcerpts, cx);
10692    });
10693    cx.executor().run_until_parked();
10694    workspace
10695        .update(cx, |workspace, cx| {
10696            let active_item = workspace
10697                .active_item(cx)
10698                .expect("should have an active item after navigating into the 3rd buffer");
10699            let third_item_id = active_item.item_id();
10700            assert_ne!(
10701                third_item_id, multibuffer_item_id,
10702                "Should navigate into the 3rd buffer and activate it"
10703            );
10704            assert_ne!(third_item_id, first_item_id);
10705            assert_ne!(third_item_id, second_item_id);
10706            assert!(
10707                active_item.is_singleton(cx),
10708                "New active item should be a singleton buffer"
10709            );
10710            assert_eq!(
10711                active_item
10712                    .act_as::<Editor>(cx)
10713                    .expect("should have navigated into an editor")
10714                    .read(cx)
10715                    .text(cx),
10716                sample_text_3
10717            );
10718
10719            workspace
10720                .go_back(workspace.active_pane().downgrade(), cx)
10721                .detach_and_log_err(cx);
10722        })
10723        .unwrap();
10724    cx.executor().run_until_parked();
10725    workspace
10726        .update(cx, |workspace, cx| {
10727            let active_item = workspace
10728                .active_item(cx)
10729                .expect("should have an active item after navigating back from the 3rd buffer");
10730            assert_eq!(
10731                active_item.item_id(),
10732                multibuffer_item_id,
10733                "Should navigate back from the 3rd buffer to the multi buffer"
10734            );
10735            assert!(!active_item.is_singleton(cx));
10736        })
10737        .unwrap();
10738}
10739
10740#[gpui::test]
10741async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10742    init_test(cx, |_| {});
10743
10744    let mut cx = EditorTestContext::new(cx).await;
10745
10746    let diff_base = r#"
10747        use some::mod;
10748
10749        const A: u32 = 42;
10750
10751        fn main() {
10752            println!("hello");
10753
10754            println!("world");
10755        }
10756        "#
10757    .unindent();
10758
10759    cx.set_state(
10760        &r#"
10761        use some::modified;
10762
10763        ˇ
10764        fn main() {
10765            println!("hello there");
10766
10767            println!("around the");
10768            println!("world");
10769        }
10770        "#
10771        .unindent(),
10772    );
10773
10774    cx.set_diff_base(Some(&diff_base));
10775    executor.run_until_parked();
10776    let unexpanded_hunks = vec![
10777        (
10778            "use some::mod;\n".to_string(),
10779            DiffHunkStatus::Modified,
10780            DisplayRow(0)..DisplayRow(1),
10781        ),
10782        (
10783            "const A: u32 = 42;\n".to_string(),
10784            DiffHunkStatus::Removed,
10785            DisplayRow(2)..DisplayRow(2),
10786        ),
10787        (
10788            "    println!(\"hello\");\n".to_string(),
10789            DiffHunkStatus::Modified,
10790            DisplayRow(4)..DisplayRow(5),
10791        ),
10792        (
10793            "".to_string(),
10794            DiffHunkStatus::Added,
10795            DisplayRow(6)..DisplayRow(7),
10796        ),
10797    ];
10798    cx.update_editor(|editor, cx| {
10799        let snapshot = editor.snapshot(cx);
10800        let all_hunks = editor_hunks(editor, &snapshot, cx);
10801        assert_eq!(all_hunks, unexpanded_hunks);
10802    });
10803
10804    cx.update_editor(|editor, cx| {
10805        for _ in 0..4 {
10806            editor.go_to_hunk(&GoToHunk, cx);
10807            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10808        }
10809    });
10810    executor.run_until_parked();
10811    cx.assert_editor_state(
10812        &r#"
10813        use some::modified;
10814
10815        ˇ
10816        fn main() {
10817            println!("hello there");
10818
10819            println!("around the");
10820            println!("world");
10821        }
10822        "#
10823        .unindent(),
10824    );
10825    cx.update_editor(|editor, cx| {
10826        let snapshot = editor.snapshot(cx);
10827        let all_hunks = editor_hunks(editor, &snapshot, cx);
10828        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10829        assert_eq!(
10830            expanded_hunks_background_highlights(editor, cx),
10831            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10832            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10833        );
10834        assert_eq!(
10835            all_hunks,
10836            vec![
10837                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10838                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10839                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10840                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10841            ],
10842            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10843            (from modified and removed hunks)"
10844        );
10845        assert_eq!(
10846            all_hunks, all_expanded_hunks,
10847            "Editor hunks should not change and all be expanded"
10848        );
10849    });
10850
10851    cx.update_editor(|editor, cx| {
10852        editor.cancel(&Cancel, cx);
10853
10854        let snapshot = editor.snapshot(cx);
10855        let all_hunks = editor_hunks(editor, &snapshot, cx);
10856        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10857        assert_eq!(
10858            expanded_hunks_background_highlights(editor, cx),
10859            Vec::new(),
10860            "After cancelling in editor, no git highlights should be left"
10861        );
10862        assert_eq!(
10863            all_expanded_hunks,
10864            Vec::new(),
10865            "After cancelling in editor, no hunks should be expanded"
10866        );
10867        assert_eq!(
10868            all_hunks, unexpanded_hunks,
10869            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10870        );
10871    });
10872}
10873
10874#[gpui::test]
10875async fn test_toggled_diff_base_change(
10876    executor: BackgroundExecutor,
10877    cx: &mut gpui::TestAppContext,
10878) {
10879    init_test(cx, |_| {});
10880
10881    let mut cx = EditorTestContext::new(cx).await;
10882
10883    let diff_base = r#"
10884        use some::mod1;
10885        use some::mod2;
10886
10887        const A: u32 = 42;
10888        const B: u32 = 42;
10889        const C: u32 = 42;
10890
10891        fn main(ˇ) {
10892            println!("hello");
10893
10894            println!("world");
10895        }
10896        "#
10897    .unindent();
10898
10899    cx.set_state(
10900        &r#"
10901        use some::mod2;
10902
10903        const A: u32 = 42;
10904        const C: u32 = 42;
10905
10906        fn main(ˇ) {
10907            //println!("hello");
10908
10909            println!("world");
10910            //
10911            //
10912        }
10913        "#
10914        .unindent(),
10915    );
10916
10917    cx.set_diff_base(Some(&diff_base));
10918    executor.run_until_parked();
10919    cx.update_editor(|editor, cx| {
10920        let snapshot = editor.snapshot(cx);
10921        let all_hunks = editor_hunks(editor, &snapshot, cx);
10922        assert_eq!(
10923            all_hunks,
10924            vec![
10925                (
10926                    "use some::mod1;\n".to_string(),
10927                    DiffHunkStatus::Removed,
10928                    DisplayRow(0)..DisplayRow(0)
10929                ),
10930                (
10931                    "const B: u32 = 42;\n".to_string(),
10932                    DiffHunkStatus::Removed,
10933                    DisplayRow(3)..DisplayRow(3)
10934                ),
10935                (
10936                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10937                    DiffHunkStatus::Modified,
10938                    DisplayRow(5)..DisplayRow(7)
10939                ),
10940                (
10941                    "".to_string(),
10942                    DiffHunkStatus::Added,
10943                    DisplayRow(9)..DisplayRow(11)
10944                ),
10945            ]
10946        );
10947    });
10948
10949    cx.update_editor(|editor, cx| {
10950        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10951    });
10952    executor.run_until_parked();
10953    cx.assert_editor_state(
10954        &r#"
10955        use some::mod2;
10956
10957        const A: u32 = 42;
10958        const C: u32 = 42;
10959
10960        fn main(ˇ) {
10961            //println!("hello");
10962
10963            println!("world");
10964            //
10965            //
10966        }
10967        "#
10968        .unindent(),
10969    );
10970    cx.update_editor(|editor, cx| {
10971        let snapshot = editor.snapshot(cx);
10972        let all_hunks = editor_hunks(editor, &snapshot, cx);
10973        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10974        assert_eq!(
10975            expanded_hunks_background_highlights(editor, cx),
10976            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10977            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10978        );
10979        assert_eq!(
10980            all_hunks,
10981            vec![
10982                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10983                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10984                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10985                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10986            ],
10987            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10988            (from modified and removed hunks)"
10989        );
10990        assert_eq!(
10991            all_hunks, all_expanded_hunks,
10992            "Editor hunks should not change and all be expanded"
10993        );
10994    });
10995
10996    cx.set_diff_base(Some("new diff base!"));
10997    executor.run_until_parked();
10998
10999    cx.update_editor(|editor, cx| {
11000        let snapshot = editor.snapshot(cx);
11001        let all_hunks = editor_hunks(editor, &snapshot, cx);
11002        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11003        assert_eq!(
11004            expanded_hunks_background_highlights(editor, cx),
11005            Vec::new(),
11006            "After diff base is changed, old git highlights should be removed"
11007        );
11008        assert_eq!(
11009            all_expanded_hunks,
11010            Vec::new(),
11011            "After diff base is changed, old git hunk expansions should be removed"
11012        );
11013        assert_eq!(
11014            all_hunks,
11015            vec![(
11016                "new diff base!".to_string(),
11017                DiffHunkStatus::Modified,
11018                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
11019            )],
11020            "After diff base is changed, hunks should update"
11021        );
11022    });
11023}
11024
11025#[gpui::test]
11026async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11027    init_test(cx, |_| {});
11028
11029    let mut cx = EditorTestContext::new(cx).await;
11030
11031    let diff_base = r#"
11032        use some::mod1;
11033        use some::mod2;
11034
11035        const A: u32 = 42;
11036        const B: u32 = 42;
11037        const C: u32 = 42;
11038
11039        fn main(ˇ) {
11040            println!("hello");
11041
11042            println!("world");
11043        }
11044
11045        fn another() {
11046            println!("another");
11047        }
11048
11049        fn another2() {
11050            println!("another2");
11051        }
11052        "#
11053    .unindent();
11054
11055    cx.set_state(
11056        &r#"
11057        «use some::mod2;
11058
11059        const A: u32 = 42;
11060        const C: u32 = 42;
11061
11062        fn main() {
11063            //println!("hello");
11064
11065            println!("world");
11066            //
11067            //ˇ»
11068        }
11069
11070        fn another() {
11071            println!("another");
11072            println!("another");
11073        }
11074
11075            println!("another2");
11076        }
11077        "#
11078        .unindent(),
11079    );
11080
11081    cx.set_diff_base(Some(&diff_base));
11082    executor.run_until_parked();
11083    cx.update_editor(|editor, cx| {
11084        let snapshot = editor.snapshot(cx);
11085        let all_hunks = editor_hunks(editor, &snapshot, cx);
11086        assert_eq!(
11087            all_hunks,
11088            vec![
11089                (
11090                    "use some::mod1;\n".to_string(),
11091                    DiffHunkStatus::Removed,
11092                    DisplayRow(0)..DisplayRow(0)
11093                ),
11094                (
11095                    "const B: u32 = 42;\n".to_string(),
11096                    DiffHunkStatus::Removed,
11097                    DisplayRow(3)..DisplayRow(3)
11098                ),
11099                (
11100                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11101                    DiffHunkStatus::Modified,
11102                    DisplayRow(5)..DisplayRow(7)
11103                ),
11104                (
11105                    "".to_string(),
11106                    DiffHunkStatus::Added,
11107                    DisplayRow(9)..DisplayRow(11)
11108                ),
11109                (
11110                    "".to_string(),
11111                    DiffHunkStatus::Added,
11112                    DisplayRow(15)..DisplayRow(16)
11113                ),
11114                (
11115                    "fn another2() {\n".to_string(),
11116                    DiffHunkStatus::Removed,
11117                    DisplayRow(18)..DisplayRow(18)
11118                ),
11119            ]
11120        );
11121    });
11122
11123    cx.update_editor(|editor, cx| {
11124        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11125    });
11126    executor.run_until_parked();
11127    cx.assert_editor_state(
11128        &r#"
11129        «use some::mod2;
11130
11131        const A: u32 = 42;
11132        const C: u32 = 42;
11133
11134        fn main() {
11135            //println!("hello");
11136
11137            println!("world");
11138            //
11139            //ˇ»
11140        }
11141
11142        fn another() {
11143            println!("another");
11144            println!("another");
11145        }
11146
11147            println!("another2");
11148        }
11149        "#
11150        .unindent(),
11151    );
11152    cx.update_editor(|editor, cx| {
11153        let snapshot = editor.snapshot(cx);
11154        let all_hunks = editor_hunks(editor, &snapshot, cx);
11155        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11156        assert_eq!(
11157            expanded_hunks_background_highlights(editor, cx),
11158            vec![
11159                DisplayRow(9)..=DisplayRow(10),
11160                DisplayRow(13)..=DisplayRow(14),
11161                DisplayRow(19)..=DisplayRow(19)
11162            ]
11163        );
11164        assert_eq!(
11165            all_hunks,
11166            vec![
11167                (
11168                    "use some::mod1;\n".to_string(),
11169                    DiffHunkStatus::Removed,
11170                    DisplayRow(1)..DisplayRow(1)
11171                ),
11172                (
11173                    "const B: u32 = 42;\n".to_string(),
11174                    DiffHunkStatus::Removed,
11175                    DisplayRow(5)..DisplayRow(5)
11176                ),
11177                (
11178                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11179                    DiffHunkStatus::Modified,
11180                    DisplayRow(9)..DisplayRow(11)
11181                ),
11182                (
11183                    "".to_string(),
11184                    DiffHunkStatus::Added,
11185                    DisplayRow(13)..DisplayRow(15)
11186                ),
11187                (
11188                    "".to_string(),
11189                    DiffHunkStatus::Added,
11190                    DisplayRow(19)..DisplayRow(20)
11191                ),
11192                (
11193                    "fn another2() {\n".to_string(),
11194                    DiffHunkStatus::Removed,
11195                    DisplayRow(23)..DisplayRow(23)
11196                ),
11197            ],
11198        );
11199        assert_eq!(all_hunks, all_expanded_hunks);
11200    });
11201
11202    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11203    cx.executor().run_until_parked();
11204    cx.assert_editor_state(
11205        &r#"
11206        «use some::mod2;
11207
11208        const A: u32 = 42;
11209        const C: u32 = 42;
11210
11211        fn main() {
11212            //println!("hello");
11213
11214            println!("world");
11215            //
11216            //ˇ»
11217        }
11218
11219        fn another() {
11220            println!("another");
11221            println!("another");
11222        }
11223
11224            println!("another2");
11225        }
11226        "#
11227        .unindent(),
11228    );
11229    cx.update_editor(|editor, cx| {
11230        let snapshot = editor.snapshot(cx);
11231        let all_hunks = editor_hunks(editor, &snapshot, cx);
11232        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11233        assert_eq!(
11234            expanded_hunks_background_highlights(editor, cx),
11235            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11236            "Only one hunk is left not folded, its highlight should be visible"
11237        );
11238        assert_eq!(
11239            all_hunks,
11240            vec![
11241                (
11242                    "use some::mod1;\n".to_string(),
11243                    DiffHunkStatus::Removed,
11244                    DisplayRow(0)..DisplayRow(0)
11245                ),
11246                (
11247                    "const B: u32 = 42;\n".to_string(),
11248                    DiffHunkStatus::Removed,
11249                    DisplayRow(0)..DisplayRow(0)
11250                ),
11251                (
11252                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11253                    DiffHunkStatus::Modified,
11254                    DisplayRow(0)..DisplayRow(0)
11255                ),
11256                (
11257                    "".to_string(),
11258                    DiffHunkStatus::Added,
11259                    DisplayRow(0)..DisplayRow(1)
11260                ),
11261                (
11262                    "".to_string(),
11263                    DiffHunkStatus::Added,
11264                    DisplayRow(5)..DisplayRow(6)
11265                ),
11266                (
11267                    "fn another2() {\n".to_string(),
11268                    DiffHunkStatus::Removed,
11269                    DisplayRow(9)..DisplayRow(9)
11270                ),
11271            ],
11272            "Hunk list should still return shifted folded hunks"
11273        );
11274        assert_eq!(
11275            all_expanded_hunks,
11276            vec![
11277                (
11278                    "".to_string(),
11279                    DiffHunkStatus::Added,
11280                    DisplayRow(5)..DisplayRow(6)
11281                ),
11282                (
11283                    "fn another2() {\n".to_string(),
11284                    DiffHunkStatus::Removed,
11285                    DisplayRow(9)..DisplayRow(9)
11286                ),
11287            ],
11288            "Only non-folded hunks should be left expanded"
11289        );
11290    });
11291
11292    cx.update_editor(|editor, cx| {
11293        editor.select_all(&SelectAll, cx);
11294        editor.unfold_lines(&UnfoldLines, cx);
11295    });
11296    cx.executor().run_until_parked();
11297    cx.assert_editor_state(
11298        &r#"
11299        «use some::mod2;
11300
11301        const A: u32 = 42;
11302        const C: u32 = 42;
11303
11304        fn main() {
11305            //println!("hello");
11306
11307            println!("world");
11308            //
11309            //
11310        }
11311
11312        fn another() {
11313            println!("another");
11314            println!("another");
11315        }
11316
11317            println!("another2");
11318        }
11319        ˇ»"#
11320        .unindent(),
11321    );
11322    cx.update_editor(|editor, cx| {
11323        let snapshot = editor.snapshot(cx);
11324        let all_hunks = editor_hunks(editor, &snapshot, cx);
11325        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11326        assert_eq!(
11327            expanded_hunks_background_highlights(editor, cx),
11328            vec![
11329                DisplayRow(9)..=DisplayRow(10),
11330                DisplayRow(13)..=DisplayRow(14),
11331                DisplayRow(19)..=DisplayRow(19)
11332            ],
11333            "After unfolding, all hunk diffs should be visible again"
11334        );
11335        assert_eq!(
11336            all_hunks,
11337            vec![
11338                (
11339                    "use some::mod1;\n".to_string(),
11340                    DiffHunkStatus::Removed,
11341                    DisplayRow(1)..DisplayRow(1)
11342                ),
11343                (
11344                    "const B: u32 = 42;\n".to_string(),
11345                    DiffHunkStatus::Removed,
11346                    DisplayRow(5)..DisplayRow(5)
11347                ),
11348                (
11349                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11350                    DiffHunkStatus::Modified,
11351                    DisplayRow(9)..DisplayRow(11)
11352                ),
11353                (
11354                    "".to_string(),
11355                    DiffHunkStatus::Added,
11356                    DisplayRow(13)..DisplayRow(15)
11357                ),
11358                (
11359                    "".to_string(),
11360                    DiffHunkStatus::Added,
11361                    DisplayRow(19)..DisplayRow(20)
11362                ),
11363                (
11364                    "fn another2() {\n".to_string(),
11365                    DiffHunkStatus::Removed,
11366                    DisplayRow(23)..DisplayRow(23)
11367                ),
11368            ],
11369        );
11370        assert_eq!(all_hunks, all_expanded_hunks);
11371    });
11372}
11373
11374#[gpui::test]
11375async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11376    init_test(cx, |_| {});
11377
11378    let cols = 4;
11379    let rows = 10;
11380    let sample_text_1 = sample_text(rows, cols, 'a');
11381    assert_eq!(
11382        sample_text_1,
11383        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11384    );
11385    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11386    let sample_text_2 = sample_text(rows, cols, 'l');
11387    assert_eq!(
11388        sample_text_2,
11389        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11390    );
11391    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11392    let sample_text_3 = sample_text(rows, cols, 'v');
11393    assert_eq!(
11394        sample_text_3,
11395        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11396    );
11397    let modified_sample_text_3 =
11398        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11399    let buffer_1 = cx.new_model(|cx| {
11400        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11401        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11402        buffer
11403    });
11404    let buffer_2 = cx.new_model(|cx| {
11405        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11406        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11407        buffer
11408    });
11409    let buffer_3 = cx.new_model(|cx| {
11410        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11411        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11412        buffer
11413    });
11414
11415    let multi_buffer = cx.new_model(|cx| {
11416        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11417        multibuffer.push_excerpts(
11418            buffer_1.clone(),
11419            [
11420                ExcerptRange {
11421                    context: Point::new(0, 0)..Point::new(3, 0),
11422                    primary: None,
11423                },
11424                ExcerptRange {
11425                    context: Point::new(5, 0)..Point::new(7, 0),
11426                    primary: None,
11427                },
11428                ExcerptRange {
11429                    context: Point::new(9, 0)..Point::new(10, 4),
11430                    primary: None,
11431                },
11432            ],
11433            cx,
11434        );
11435        multibuffer.push_excerpts(
11436            buffer_2.clone(),
11437            [
11438                ExcerptRange {
11439                    context: Point::new(0, 0)..Point::new(3, 0),
11440                    primary: None,
11441                },
11442                ExcerptRange {
11443                    context: Point::new(5, 0)..Point::new(7, 0),
11444                    primary: None,
11445                },
11446                ExcerptRange {
11447                    context: Point::new(9, 0)..Point::new(10, 4),
11448                    primary: None,
11449                },
11450            ],
11451            cx,
11452        );
11453        multibuffer.push_excerpts(
11454            buffer_3.clone(),
11455            [
11456                ExcerptRange {
11457                    context: Point::new(0, 0)..Point::new(3, 0),
11458                    primary: None,
11459                },
11460                ExcerptRange {
11461                    context: Point::new(5, 0)..Point::new(7, 0),
11462                    primary: None,
11463                },
11464                ExcerptRange {
11465                    context: Point::new(9, 0)..Point::new(10, 4),
11466                    primary: None,
11467                },
11468            ],
11469            cx,
11470        );
11471        multibuffer
11472    });
11473
11474    let fs = FakeFs::new(cx.executor());
11475    fs.insert_tree(
11476        "/a",
11477        json!({
11478            "main.rs": modified_sample_text_1,
11479            "other.rs": modified_sample_text_2,
11480            "lib.rs": modified_sample_text_3,
11481        }),
11482    )
11483    .await;
11484
11485    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11486    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11487    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11488    let multi_buffer_editor = cx.new_view(|cx| {
11489        Editor::new(
11490            EditorMode::Full,
11491            multi_buffer,
11492            Some(project.clone()),
11493            true,
11494            cx,
11495        )
11496    });
11497    cx.executor().run_until_parked();
11498
11499    let expected_all_hunks = vec![
11500        (
11501            "bbbb\n".to_string(),
11502            DiffHunkStatus::Removed,
11503            DisplayRow(4)..DisplayRow(4),
11504        ),
11505        (
11506            "nnnn\n".to_string(),
11507            DiffHunkStatus::Modified,
11508            DisplayRow(21)..DisplayRow(22),
11509        ),
11510        (
11511            "".to_string(),
11512            DiffHunkStatus::Added,
11513            DisplayRow(41)..DisplayRow(42),
11514        ),
11515    ];
11516    let expected_all_hunks_shifted = vec![
11517        (
11518            "bbbb\n".to_string(),
11519            DiffHunkStatus::Removed,
11520            DisplayRow(5)..DisplayRow(5),
11521        ),
11522        (
11523            "nnnn\n".to_string(),
11524            DiffHunkStatus::Modified,
11525            DisplayRow(23)..DisplayRow(24),
11526        ),
11527        (
11528            "".to_string(),
11529            DiffHunkStatus::Added,
11530            DisplayRow(43)..DisplayRow(44),
11531        ),
11532    ];
11533
11534    multi_buffer_editor.update(cx, |editor, cx| {
11535        let snapshot = editor.snapshot(cx);
11536        let all_hunks = editor_hunks(editor, &snapshot, cx);
11537        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11538        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11539        assert_eq!(all_hunks, expected_all_hunks);
11540        assert_eq!(all_expanded_hunks, Vec::new());
11541    });
11542
11543    multi_buffer_editor.update(cx, |editor, cx| {
11544        editor.select_all(&SelectAll, cx);
11545        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11546    });
11547    cx.executor().run_until_parked();
11548    multi_buffer_editor.update(cx, |editor, cx| {
11549        let snapshot = editor.snapshot(cx);
11550        let all_hunks = editor_hunks(editor, &snapshot, cx);
11551        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11552        assert_eq!(
11553            expanded_hunks_background_highlights(editor, cx),
11554            vec![
11555                DisplayRow(23)..=DisplayRow(23),
11556                DisplayRow(43)..=DisplayRow(43)
11557            ],
11558        );
11559        assert_eq!(all_hunks, expected_all_hunks_shifted);
11560        assert_eq!(all_hunks, all_expanded_hunks);
11561    });
11562
11563    multi_buffer_editor.update(cx, |editor, cx| {
11564        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11565    });
11566    cx.executor().run_until_parked();
11567    multi_buffer_editor.update(cx, |editor, cx| {
11568        let snapshot = editor.snapshot(cx);
11569        let all_hunks = editor_hunks(editor, &snapshot, cx);
11570        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11571        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11572        assert_eq!(all_hunks, expected_all_hunks);
11573        assert_eq!(all_expanded_hunks, Vec::new());
11574    });
11575
11576    multi_buffer_editor.update(cx, |editor, cx| {
11577        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11578    });
11579    cx.executor().run_until_parked();
11580    multi_buffer_editor.update(cx, |editor, cx| {
11581        let snapshot = editor.snapshot(cx);
11582        let all_hunks = editor_hunks(editor, &snapshot, cx);
11583        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11584        assert_eq!(
11585            expanded_hunks_background_highlights(editor, cx),
11586            vec![
11587                DisplayRow(23)..=DisplayRow(23),
11588                DisplayRow(43)..=DisplayRow(43)
11589            ],
11590        );
11591        assert_eq!(all_hunks, expected_all_hunks_shifted);
11592        assert_eq!(all_hunks, all_expanded_hunks);
11593    });
11594
11595    multi_buffer_editor.update(cx, |editor, cx| {
11596        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11597    });
11598    cx.executor().run_until_parked();
11599    multi_buffer_editor.update(cx, |editor, cx| {
11600        let snapshot = editor.snapshot(cx);
11601        let all_hunks = editor_hunks(editor, &snapshot, cx);
11602        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11603        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11604        assert_eq!(all_hunks, expected_all_hunks);
11605        assert_eq!(all_expanded_hunks, Vec::new());
11606    });
11607}
11608
11609#[gpui::test]
11610async fn test_edits_around_toggled_additions(
11611    executor: BackgroundExecutor,
11612    cx: &mut gpui::TestAppContext,
11613) {
11614    init_test(cx, |_| {});
11615
11616    let mut cx = EditorTestContext::new(cx).await;
11617
11618    let diff_base = r#"
11619        use some::mod1;
11620        use some::mod2;
11621
11622        const A: u32 = 42;
11623
11624        fn main() {
11625            println!("hello");
11626
11627            println!("world");
11628        }
11629        "#
11630    .unindent();
11631    executor.run_until_parked();
11632    cx.set_state(
11633        &r#"
11634        use some::mod1;
11635        use some::mod2;
11636
11637        const A: u32 = 42;
11638        const B: u32 = 42;
11639        const C: u32 = 42;
11640        ˇ
11641
11642        fn main() {
11643            println!("hello");
11644
11645            println!("world");
11646        }
11647        "#
11648        .unindent(),
11649    );
11650
11651    cx.set_diff_base(Some(&diff_base));
11652    executor.run_until_parked();
11653    cx.update_editor(|editor, cx| {
11654        let snapshot = editor.snapshot(cx);
11655        let all_hunks = editor_hunks(editor, &snapshot, cx);
11656        assert_eq!(
11657            all_hunks,
11658            vec![(
11659                "".to_string(),
11660                DiffHunkStatus::Added,
11661                DisplayRow(4)..DisplayRow(7)
11662            )]
11663        );
11664    });
11665    cx.update_editor(|editor, cx| {
11666        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11667    });
11668    executor.run_until_parked();
11669    cx.assert_editor_state(
11670        &r#"
11671        use some::mod1;
11672        use some::mod2;
11673
11674        const A: u32 = 42;
11675        const B: u32 = 42;
11676        const C: u32 = 42;
11677        ˇ
11678
11679        fn main() {
11680            println!("hello");
11681
11682            println!("world");
11683        }
11684        "#
11685        .unindent(),
11686    );
11687    cx.update_editor(|editor, cx| {
11688        let snapshot = editor.snapshot(cx);
11689        let all_hunks = editor_hunks(editor, &snapshot, cx);
11690        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11691        assert_eq!(
11692            all_hunks,
11693            vec![(
11694                "".to_string(),
11695                DiffHunkStatus::Added,
11696                DisplayRow(4)..DisplayRow(7)
11697            )]
11698        );
11699        assert_eq!(
11700            expanded_hunks_background_highlights(editor, cx),
11701            vec![DisplayRow(4)..=DisplayRow(6)]
11702        );
11703        assert_eq!(all_hunks, all_expanded_hunks);
11704    });
11705
11706    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11707    executor.run_until_parked();
11708    cx.assert_editor_state(
11709        &r#"
11710        use some::mod1;
11711        use some::mod2;
11712
11713        const A: u32 = 42;
11714        const B: u32 = 42;
11715        const C: u32 = 42;
11716        const D: u32 = 42;
11717        ˇ
11718
11719        fn main() {
11720            println!("hello");
11721
11722            println!("world");
11723        }
11724        "#
11725        .unindent(),
11726    );
11727    cx.update_editor(|editor, cx| {
11728        let snapshot = editor.snapshot(cx);
11729        let all_hunks = editor_hunks(editor, &snapshot, cx);
11730        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11731        assert_eq!(
11732            all_hunks,
11733            vec![(
11734                "".to_string(),
11735                DiffHunkStatus::Added,
11736                DisplayRow(4)..DisplayRow(8)
11737            )]
11738        );
11739        assert_eq!(
11740            expanded_hunks_background_highlights(editor, cx),
11741            vec![DisplayRow(4)..=DisplayRow(6)],
11742            "Edited hunk should have one more line added"
11743        );
11744        assert_eq!(
11745            all_hunks, all_expanded_hunks,
11746            "Expanded hunk should also grow with the addition"
11747        );
11748    });
11749
11750    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11751    executor.run_until_parked();
11752    cx.assert_editor_state(
11753        &r#"
11754        use some::mod1;
11755        use some::mod2;
11756
11757        const A: u32 = 42;
11758        const B: u32 = 42;
11759        const C: u32 = 42;
11760        const D: u32 = 42;
11761        const E: u32 = 42;
11762        ˇ
11763
11764        fn main() {
11765            println!("hello");
11766
11767            println!("world");
11768        }
11769        "#
11770        .unindent(),
11771    );
11772    cx.update_editor(|editor, cx| {
11773        let snapshot = editor.snapshot(cx);
11774        let all_hunks = editor_hunks(editor, &snapshot, cx);
11775        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11776        assert_eq!(
11777            all_hunks,
11778            vec![(
11779                "".to_string(),
11780                DiffHunkStatus::Added,
11781                DisplayRow(4)..DisplayRow(9)
11782            )]
11783        );
11784        assert_eq!(
11785            expanded_hunks_background_highlights(editor, cx),
11786            vec![DisplayRow(4)..=DisplayRow(6)],
11787            "Edited hunk should have one more line added"
11788        );
11789        assert_eq!(all_hunks, all_expanded_hunks);
11790    });
11791
11792    cx.update_editor(|editor, cx| {
11793        editor.move_up(&MoveUp, cx);
11794        editor.delete_line(&DeleteLine, cx);
11795    });
11796    executor.run_until_parked();
11797    cx.assert_editor_state(
11798        &r#"
11799        use some::mod1;
11800        use some::mod2;
11801
11802        const A: u32 = 42;
11803        const B: u32 = 42;
11804        const C: u32 = 42;
11805        const D: u32 = 42;
11806        ˇ
11807
11808        fn main() {
11809            println!("hello");
11810
11811            println!("world");
11812        }
11813        "#
11814        .unindent(),
11815    );
11816    cx.update_editor(|editor, cx| {
11817        let snapshot = editor.snapshot(cx);
11818        let all_hunks = editor_hunks(editor, &snapshot, cx);
11819        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11820        assert_eq!(
11821            all_hunks,
11822            vec![(
11823                "".to_string(),
11824                DiffHunkStatus::Added,
11825                DisplayRow(4)..DisplayRow(8)
11826            )]
11827        );
11828        assert_eq!(
11829            expanded_hunks_background_highlights(editor, cx),
11830            vec![DisplayRow(4)..=DisplayRow(6)],
11831            "Deleting a line should shrint the hunk"
11832        );
11833        assert_eq!(
11834            all_hunks, all_expanded_hunks,
11835            "Expanded hunk should also shrink with the addition"
11836        );
11837    });
11838
11839    cx.update_editor(|editor, cx| {
11840        editor.move_up(&MoveUp, cx);
11841        editor.delete_line(&DeleteLine, cx);
11842        editor.move_up(&MoveUp, cx);
11843        editor.delete_line(&DeleteLine, cx);
11844        editor.move_up(&MoveUp, cx);
11845        editor.delete_line(&DeleteLine, cx);
11846    });
11847    executor.run_until_parked();
11848    cx.assert_editor_state(
11849        &r#"
11850        use some::mod1;
11851        use some::mod2;
11852
11853        const A: u32 = 42;
11854        ˇ
11855
11856        fn main() {
11857            println!("hello");
11858
11859            println!("world");
11860        }
11861        "#
11862        .unindent(),
11863    );
11864    cx.update_editor(|editor, cx| {
11865        let snapshot = editor.snapshot(cx);
11866        let all_hunks = editor_hunks(editor, &snapshot, cx);
11867        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11868        assert_eq!(
11869            all_hunks,
11870            vec![(
11871                "".to_string(),
11872                DiffHunkStatus::Added,
11873                DisplayRow(5)..DisplayRow(6)
11874            )]
11875        );
11876        assert_eq!(
11877            expanded_hunks_background_highlights(editor, cx),
11878            vec![DisplayRow(5)..=DisplayRow(5)]
11879        );
11880        assert_eq!(all_hunks, all_expanded_hunks);
11881    });
11882
11883    cx.update_editor(|editor, cx| {
11884        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11885        editor.delete_line(&DeleteLine, cx);
11886    });
11887    executor.run_until_parked();
11888    cx.assert_editor_state(
11889        &r#"
11890        ˇ
11891
11892        fn main() {
11893            println!("hello");
11894
11895            println!("world");
11896        }
11897        "#
11898        .unindent(),
11899    );
11900    cx.update_editor(|editor, cx| {
11901        let snapshot = editor.snapshot(cx);
11902        let all_hunks = editor_hunks(editor, &snapshot, cx);
11903        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11904        assert_eq!(
11905            all_hunks,
11906            vec![
11907                (
11908                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11909                    DiffHunkStatus::Removed,
11910                    DisplayRow(0)..DisplayRow(0)
11911                ),
11912                (
11913                    "const A: u32 = 42;\n".to_string(),
11914                    DiffHunkStatus::Removed,
11915                    DisplayRow(2)..DisplayRow(2)
11916                )
11917            ]
11918        );
11919        assert_eq!(
11920            expanded_hunks_background_highlights(editor, cx),
11921            Vec::new(),
11922            "Should close all stale expanded addition hunks"
11923        );
11924        assert_eq!(
11925            all_expanded_hunks,
11926            vec![(
11927                "const A: u32 = 42;\n".to_string(),
11928                DiffHunkStatus::Removed,
11929                DisplayRow(2)..DisplayRow(2)
11930            )],
11931            "Should open hunks that were adjacent to the stale addition one"
11932        );
11933    });
11934}
11935
11936#[gpui::test]
11937async fn test_edits_around_toggled_deletions(
11938    executor: BackgroundExecutor,
11939    cx: &mut gpui::TestAppContext,
11940) {
11941    init_test(cx, |_| {});
11942
11943    let mut cx = EditorTestContext::new(cx).await;
11944
11945    let diff_base = r#"
11946        use some::mod1;
11947        use some::mod2;
11948
11949        const A: u32 = 42;
11950        const B: u32 = 42;
11951        const C: u32 = 42;
11952
11953
11954        fn main() {
11955            println!("hello");
11956
11957            println!("world");
11958        }
11959        "#
11960    .unindent();
11961    executor.run_until_parked();
11962    cx.set_state(
11963        &r#"
11964        use some::mod1;
11965        use some::mod2;
11966
11967        ˇconst B: u32 = 42;
11968        const C: u32 = 42;
11969
11970
11971        fn main() {
11972            println!("hello");
11973
11974            println!("world");
11975        }
11976        "#
11977        .unindent(),
11978    );
11979
11980    cx.set_diff_base(Some(&diff_base));
11981    executor.run_until_parked();
11982    cx.update_editor(|editor, cx| {
11983        let snapshot = editor.snapshot(cx);
11984        let all_hunks = editor_hunks(editor, &snapshot, cx);
11985        assert_eq!(
11986            all_hunks,
11987            vec![(
11988                "const A: u32 = 42;\n".to_string(),
11989                DiffHunkStatus::Removed,
11990                DisplayRow(3)..DisplayRow(3)
11991            )]
11992        );
11993    });
11994    cx.update_editor(|editor, cx| {
11995        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11996    });
11997    executor.run_until_parked();
11998    cx.assert_editor_state(
11999        &r#"
12000        use some::mod1;
12001        use some::mod2;
12002
12003        ˇconst B: u32 = 42;
12004        const C: u32 = 42;
12005
12006
12007        fn main() {
12008            println!("hello");
12009
12010            println!("world");
12011        }
12012        "#
12013        .unindent(),
12014    );
12015    cx.update_editor(|editor, cx| {
12016        let snapshot = editor.snapshot(cx);
12017        let all_hunks = editor_hunks(editor, &snapshot, cx);
12018        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12019        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12020        assert_eq!(
12021            all_hunks,
12022            vec![(
12023                "const A: u32 = 42;\n".to_string(),
12024                DiffHunkStatus::Removed,
12025                DisplayRow(4)..DisplayRow(4)
12026            )]
12027        );
12028        assert_eq!(all_hunks, all_expanded_hunks);
12029    });
12030
12031    cx.update_editor(|editor, cx| {
12032        editor.delete_line(&DeleteLine, cx);
12033    });
12034    executor.run_until_parked();
12035    cx.assert_editor_state(
12036        &r#"
12037        use some::mod1;
12038        use some::mod2;
12039
12040        ˇconst C: u32 = 42;
12041
12042
12043        fn main() {
12044            println!("hello");
12045
12046            println!("world");
12047        }
12048        "#
12049        .unindent(),
12050    );
12051    cx.update_editor(|editor, cx| {
12052        let snapshot = editor.snapshot(cx);
12053        let all_hunks = editor_hunks(editor, &snapshot, cx);
12054        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12055        assert_eq!(
12056            expanded_hunks_background_highlights(editor, cx),
12057            Vec::new(),
12058            "Deleted hunks do not highlight current editor's background"
12059        );
12060        assert_eq!(
12061            all_hunks,
12062            vec![(
12063                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12064                DiffHunkStatus::Removed,
12065                DisplayRow(5)..DisplayRow(5)
12066            )]
12067        );
12068        assert_eq!(all_hunks, all_expanded_hunks);
12069    });
12070
12071    cx.update_editor(|editor, cx| {
12072        editor.delete_line(&DeleteLine, cx);
12073    });
12074    executor.run_until_parked();
12075    cx.assert_editor_state(
12076        &r#"
12077        use some::mod1;
12078        use some::mod2;
12079
12080        ˇ
12081
12082        fn main() {
12083            println!("hello");
12084
12085            println!("world");
12086        }
12087        "#
12088        .unindent(),
12089    );
12090    cx.update_editor(|editor, cx| {
12091        let snapshot = editor.snapshot(cx);
12092        let all_hunks = editor_hunks(editor, &snapshot, cx);
12093        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12094        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12095        assert_eq!(
12096            all_hunks,
12097            vec![(
12098                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12099                DiffHunkStatus::Removed,
12100                DisplayRow(6)..DisplayRow(6)
12101            )]
12102        );
12103        assert_eq!(all_hunks, all_expanded_hunks);
12104    });
12105
12106    cx.update_editor(|editor, cx| {
12107        editor.handle_input("replacement", cx);
12108    });
12109    executor.run_until_parked();
12110    cx.assert_editor_state(
12111        &r#"
12112        use some::mod1;
12113        use some::mod2;
12114
12115        replacementˇ
12116
12117        fn main() {
12118            println!("hello");
12119
12120            println!("world");
12121        }
12122        "#
12123        .unindent(),
12124    );
12125    cx.update_editor(|editor, cx| {
12126        let snapshot = editor.snapshot(cx);
12127        let all_hunks = editor_hunks(editor, &snapshot, cx);
12128        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12129        assert_eq!(
12130            all_hunks,
12131            vec![(
12132                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12133                DiffHunkStatus::Modified,
12134                DisplayRow(7)..DisplayRow(8)
12135            )]
12136        );
12137        assert_eq!(
12138            expanded_hunks_background_highlights(editor, cx),
12139            vec![DisplayRow(7)..=DisplayRow(7)],
12140            "Modified expanded hunks should display additions and highlight their background"
12141        );
12142        assert_eq!(all_hunks, all_expanded_hunks);
12143    });
12144}
12145
12146#[gpui::test]
12147async fn test_edits_around_toggled_modifications(
12148    executor: BackgroundExecutor,
12149    cx: &mut gpui::TestAppContext,
12150) {
12151    init_test(cx, |_| {});
12152
12153    let mut cx = EditorTestContext::new(cx).await;
12154
12155    let diff_base = r#"
12156        use some::mod1;
12157        use some::mod2;
12158
12159        const A: u32 = 42;
12160        const B: u32 = 42;
12161        const C: u32 = 42;
12162        const D: u32 = 42;
12163
12164
12165        fn main() {
12166            println!("hello");
12167
12168            println!("world");
12169        }"#
12170    .unindent();
12171    executor.run_until_parked();
12172    cx.set_state(
12173        &r#"
12174        use some::mod1;
12175        use some::mod2;
12176
12177        const A: u32 = 42;
12178        const B: u32 = 42;
12179        const C: u32 = 43ˇ
12180        const D: u32 = 42;
12181
12182
12183        fn main() {
12184            println!("hello");
12185
12186            println!("world");
12187        }"#
12188        .unindent(),
12189    );
12190
12191    cx.set_diff_base(Some(&diff_base));
12192    executor.run_until_parked();
12193    cx.update_editor(|editor, cx| {
12194        let snapshot = editor.snapshot(cx);
12195        let all_hunks = editor_hunks(editor, &snapshot, cx);
12196        assert_eq!(
12197            all_hunks,
12198            vec![(
12199                "const C: u32 = 42;\n".to_string(),
12200                DiffHunkStatus::Modified,
12201                DisplayRow(5)..DisplayRow(6)
12202            )]
12203        );
12204    });
12205    cx.update_editor(|editor, cx| {
12206        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12207    });
12208    executor.run_until_parked();
12209    cx.assert_editor_state(
12210        &r#"
12211        use some::mod1;
12212        use some::mod2;
12213
12214        const A: u32 = 42;
12215        const B: u32 = 42;
12216        const C: u32 = 43ˇ
12217        const D: u32 = 42;
12218
12219
12220        fn main() {
12221            println!("hello");
12222
12223            println!("world");
12224        }"#
12225        .unindent(),
12226    );
12227    cx.update_editor(|editor, cx| {
12228        let snapshot = editor.snapshot(cx);
12229        let all_hunks = editor_hunks(editor, &snapshot, cx);
12230        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12231        assert_eq!(
12232            expanded_hunks_background_highlights(editor, cx),
12233            vec![DisplayRow(6)..=DisplayRow(6)],
12234        );
12235        assert_eq!(
12236            all_hunks,
12237            vec![(
12238                "const C: u32 = 42;\n".to_string(),
12239                DiffHunkStatus::Modified,
12240                DisplayRow(6)..DisplayRow(7)
12241            )]
12242        );
12243        assert_eq!(all_hunks, all_expanded_hunks);
12244    });
12245
12246    cx.update_editor(|editor, cx| {
12247        editor.handle_input("\nnew_line\n", cx);
12248    });
12249    executor.run_until_parked();
12250    cx.assert_editor_state(
12251        &r#"
12252            use some::mod1;
12253            use some::mod2;
12254
12255            const A: u32 = 42;
12256            const B: u32 = 42;
12257            const C: u32 = 43
12258            new_line
12259            ˇ
12260            const D: u32 = 42;
12261
12262
12263            fn main() {
12264                println!("hello");
12265
12266                println!("world");
12267            }"#
12268        .unindent(),
12269    );
12270    cx.update_editor(|editor, cx| {
12271        let snapshot = editor.snapshot(cx);
12272        let all_hunks = editor_hunks(editor, &snapshot, cx);
12273        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12274        assert_eq!(
12275            expanded_hunks_background_highlights(editor, cx),
12276            vec![DisplayRow(6)..=DisplayRow(6)],
12277            "Modified hunk should grow highlighted lines on more text additions"
12278        );
12279        assert_eq!(
12280            all_hunks,
12281            vec![(
12282                "const C: u32 = 42;\n".to_string(),
12283                DiffHunkStatus::Modified,
12284                DisplayRow(6)..DisplayRow(9)
12285            )]
12286        );
12287        assert_eq!(all_hunks, all_expanded_hunks);
12288    });
12289
12290    cx.update_editor(|editor, cx| {
12291        editor.move_up(&MoveUp, cx);
12292        editor.move_up(&MoveUp, cx);
12293        editor.move_up(&MoveUp, cx);
12294        editor.delete_line(&DeleteLine, cx);
12295    });
12296    executor.run_until_parked();
12297    cx.assert_editor_state(
12298        &r#"
12299            use some::mod1;
12300            use some::mod2;
12301
12302            const A: u32 = 42;
12303            ˇconst C: u32 = 43
12304            new_line
12305
12306            const D: u32 = 42;
12307
12308
12309            fn main() {
12310                println!("hello");
12311
12312                println!("world");
12313            }"#
12314        .unindent(),
12315    );
12316    cx.update_editor(|editor, cx| {
12317        let snapshot = editor.snapshot(cx);
12318        let all_hunks = editor_hunks(editor, &snapshot, cx);
12319        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12320        assert_eq!(
12321            expanded_hunks_background_highlights(editor, cx),
12322            vec![DisplayRow(6)..=DisplayRow(8)],
12323        );
12324        assert_eq!(
12325            all_hunks,
12326            vec![(
12327                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12328                DiffHunkStatus::Modified,
12329                DisplayRow(6)..DisplayRow(9)
12330            )],
12331            "Modified hunk should grow deleted lines on text deletions above"
12332        );
12333        assert_eq!(all_hunks, all_expanded_hunks);
12334    });
12335
12336    cx.update_editor(|editor, cx| {
12337        editor.move_up(&MoveUp, cx);
12338        editor.handle_input("v", cx);
12339    });
12340    executor.run_until_parked();
12341    cx.assert_editor_state(
12342        &r#"
12343            use some::mod1;
12344            use some::mod2;
12345
12346            vˇconst A: u32 = 42;
12347            const C: u32 = 43
12348            new_line
12349
12350            const D: u32 = 42;
12351
12352
12353            fn main() {
12354                println!("hello");
12355
12356                println!("world");
12357            }"#
12358        .unindent(),
12359    );
12360    cx.update_editor(|editor, cx| {
12361        let snapshot = editor.snapshot(cx);
12362        let all_hunks = editor_hunks(editor, &snapshot, cx);
12363        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12364        assert_eq!(
12365            expanded_hunks_background_highlights(editor, cx),
12366            vec![DisplayRow(6)..=DisplayRow(9)],
12367            "Modified hunk should grow deleted lines on text modifications above"
12368        );
12369        assert_eq!(
12370            all_hunks,
12371            vec![(
12372                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12373                DiffHunkStatus::Modified,
12374                DisplayRow(6)..DisplayRow(10)
12375            )]
12376        );
12377        assert_eq!(all_hunks, all_expanded_hunks);
12378    });
12379
12380    cx.update_editor(|editor, cx| {
12381        editor.move_down(&MoveDown, cx);
12382        editor.move_down(&MoveDown, cx);
12383        editor.delete_line(&DeleteLine, cx)
12384    });
12385    executor.run_until_parked();
12386    cx.assert_editor_state(
12387        &r#"
12388            use some::mod1;
12389            use some::mod2;
12390
12391            vconst A: u32 = 42;
12392            const C: u32 = 43
12393            ˇ
12394            const D: u32 = 42;
12395
12396
12397            fn main() {
12398                println!("hello");
12399
12400                println!("world");
12401            }"#
12402        .unindent(),
12403    );
12404    cx.update_editor(|editor, cx| {
12405        let snapshot = editor.snapshot(cx);
12406        let all_hunks = editor_hunks(editor, &snapshot, cx);
12407        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12408        assert_eq!(
12409            expanded_hunks_background_highlights(editor, cx),
12410            vec![DisplayRow(6)..=DisplayRow(8)],
12411            "Modified hunk should grow shrink lines on modification lines removal"
12412        );
12413        assert_eq!(
12414            all_hunks,
12415            vec![(
12416                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12417                DiffHunkStatus::Modified,
12418                DisplayRow(6)..DisplayRow(9)
12419            )]
12420        );
12421        assert_eq!(all_hunks, all_expanded_hunks);
12422    });
12423
12424    cx.update_editor(|editor, cx| {
12425        editor.move_up(&MoveUp, cx);
12426        editor.move_up(&MoveUp, cx);
12427        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12428        editor.delete_line(&DeleteLine, cx)
12429    });
12430    executor.run_until_parked();
12431    cx.assert_editor_state(
12432        &r#"
12433            use some::mod1;
12434            use some::mod2;
12435
12436            ˇ
12437
12438            fn main() {
12439                println!("hello");
12440
12441                println!("world");
12442            }"#
12443        .unindent(),
12444    );
12445    cx.update_editor(|editor, cx| {
12446        let snapshot = editor.snapshot(cx);
12447        let all_hunks = editor_hunks(editor, &snapshot, cx);
12448        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12449        assert_eq!(
12450            expanded_hunks_background_highlights(editor, cx),
12451            Vec::new(),
12452            "Modified hunk should turn into a removed one on all modified lines removal"
12453        );
12454        assert_eq!(
12455            all_hunks,
12456            vec![(
12457                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12458                    .to_string(),
12459                DiffHunkStatus::Removed,
12460                DisplayRow(7)..DisplayRow(7)
12461            )]
12462        );
12463        assert_eq!(all_hunks, all_expanded_hunks);
12464    });
12465}
12466
12467#[gpui::test]
12468async fn test_multiple_expanded_hunks_merge(
12469    executor: BackgroundExecutor,
12470    cx: &mut gpui::TestAppContext,
12471) {
12472    init_test(cx, |_| {});
12473
12474    let mut cx = EditorTestContext::new(cx).await;
12475
12476    let diff_base = r#"
12477        use some::mod1;
12478        use some::mod2;
12479
12480        const A: u32 = 42;
12481        const B: u32 = 42;
12482        const C: u32 = 42;
12483        const D: u32 = 42;
12484
12485
12486        fn main() {
12487            println!("hello");
12488
12489            println!("world");
12490        }"#
12491    .unindent();
12492    executor.run_until_parked();
12493    cx.set_state(
12494        &r#"
12495        use some::mod1;
12496        use some::mod2;
12497
12498        const A: u32 = 42;
12499        const B: u32 = 42;
12500        const C: u32 = 43ˇ
12501        const D: u32 = 42;
12502
12503
12504        fn main() {
12505            println!("hello");
12506
12507            println!("world");
12508        }"#
12509        .unindent(),
12510    );
12511
12512    cx.set_diff_base(Some(&diff_base));
12513    executor.run_until_parked();
12514    cx.update_editor(|editor, cx| {
12515        let snapshot = editor.snapshot(cx);
12516        let all_hunks = editor_hunks(editor, &snapshot, cx);
12517        assert_eq!(
12518            all_hunks,
12519            vec![(
12520                "const C: u32 = 42;\n".to_string(),
12521                DiffHunkStatus::Modified,
12522                DisplayRow(5)..DisplayRow(6)
12523            )]
12524        );
12525    });
12526    cx.update_editor(|editor, cx| {
12527        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12528    });
12529    executor.run_until_parked();
12530    cx.assert_editor_state(
12531        &r#"
12532        use some::mod1;
12533        use some::mod2;
12534
12535        const A: u32 = 42;
12536        const B: u32 = 42;
12537        const C: u32 = 43ˇ
12538        const D: u32 = 42;
12539
12540
12541        fn main() {
12542            println!("hello");
12543
12544            println!("world");
12545        }"#
12546        .unindent(),
12547    );
12548    cx.update_editor(|editor, cx| {
12549        let snapshot = editor.snapshot(cx);
12550        let all_hunks = editor_hunks(editor, &snapshot, cx);
12551        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12552        assert_eq!(
12553            expanded_hunks_background_highlights(editor, cx),
12554            vec![DisplayRow(6)..=DisplayRow(6)],
12555        );
12556        assert_eq!(
12557            all_hunks,
12558            vec![(
12559                "const C: u32 = 42;\n".to_string(),
12560                DiffHunkStatus::Modified,
12561                DisplayRow(6)..DisplayRow(7)
12562            )]
12563        );
12564        assert_eq!(all_hunks, all_expanded_hunks);
12565    });
12566
12567    cx.update_editor(|editor, cx| {
12568        editor.handle_input("\nnew_line\n", cx);
12569    });
12570    executor.run_until_parked();
12571    cx.assert_editor_state(
12572        &r#"
12573            use some::mod1;
12574            use some::mod2;
12575
12576            const A: u32 = 42;
12577            const B: u32 = 42;
12578            const C: u32 = 43
12579            new_line
12580            ˇ
12581            const D: u32 = 42;
12582
12583
12584            fn main() {
12585                println!("hello");
12586
12587                println!("world");
12588            }"#
12589        .unindent(),
12590    );
12591}
12592
12593async fn setup_indent_guides_editor(
12594    text: &str,
12595    cx: &mut gpui::TestAppContext,
12596) -> (BufferId, EditorTestContext) {
12597    init_test(cx, |_| {});
12598
12599    let mut cx = EditorTestContext::new(cx).await;
12600
12601    let buffer_id = cx.update_editor(|editor, cx| {
12602        editor.set_text(text, cx);
12603        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12604        let buffer_id = buffer_ids[0];
12605        buffer_id
12606    });
12607
12608    (buffer_id, cx)
12609}
12610
12611fn assert_indent_guides(
12612    range: Range<u32>,
12613    expected: Vec<IndentGuide>,
12614    active_indices: Option<Vec<usize>>,
12615    cx: &mut EditorTestContext,
12616) {
12617    let indent_guides = cx.update_editor(|editor, cx| {
12618        let snapshot = editor.snapshot(cx).display_snapshot;
12619        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12620            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12621            true,
12622            &snapshot,
12623            cx,
12624        );
12625
12626        indent_guides.sort_by(|a, b| {
12627            a.depth.cmp(&b.depth).then(
12628                a.start_row
12629                    .cmp(&b.start_row)
12630                    .then(a.end_row.cmp(&b.end_row)),
12631            )
12632        });
12633        indent_guides
12634    });
12635
12636    if let Some(expected) = active_indices {
12637        let active_indices = cx.update_editor(|editor, cx| {
12638            let snapshot = editor.snapshot(cx).display_snapshot;
12639            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12640        });
12641
12642        assert_eq!(
12643            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12644            expected,
12645            "Active indent guide indices do not match"
12646        );
12647    }
12648
12649    let expected: Vec<_> = expected
12650        .into_iter()
12651        .map(|guide| MultiBufferIndentGuide {
12652            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12653            buffer: guide,
12654        })
12655        .collect();
12656
12657    assert_eq!(indent_guides, expected, "Indent guides do not match");
12658}
12659
12660fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12661    IndentGuide {
12662        buffer_id,
12663        start_row,
12664        end_row,
12665        depth,
12666        tab_size: 4,
12667        settings: IndentGuideSettings {
12668            enabled: true,
12669            line_width: 1,
12670            active_line_width: 1,
12671            ..Default::default()
12672        },
12673    }
12674}
12675
12676#[gpui::test]
12677async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12678    let (buffer_id, mut cx) = setup_indent_guides_editor(
12679        &"
12680    fn main() {
12681        let a = 1;
12682    }"
12683        .unindent(),
12684        cx,
12685    )
12686    .await;
12687
12688    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12689}
12690
12691#[gpui::test]
12692async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12693    let (buffer_id, mut cx) = setup_indent_guides_editor(
12694        &"
12695    fn main() {
12696        let a = 1;
12697        let b = 2;
12698    }"
12699        .unindent(),
12700        cx,
12701    )
12702    .await;
12703
12704    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12705}
12706
12707#[gpui::test]
12708async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12709    let (buffer_id, mut cx) = setup_indent_guides_editor(
12710        &"
12711    fn main() {
12712        let a = 1;
12713        if a == 3 {
12714            let b = 2;
12715        } else {
12716            let c = 3;
12717        }
12718    }"
12719        .unindent(),
12720        cx,
12721    )
12722    .await;
12723
12724    assert_indent_guides(
12725        0..8,
12726        vec![
12727            indent_guide(buffer_id, 1, 6, 0),
12728            indent_guide(buffer_id, 3, 3, 1),
12729            indent_guide(buffer_id, 5, 5, 1),
12730        ],
12731        None,
12732        &mut cx,
12733    );
12734}
12735
12736#[gpui::test]
12737async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12738    let (buffer_id, mut cx) = setup_indent_guides_editor(
12739        &"
12740    fn main() {
12741        let a = 1;
12742            let b = 2;
12743        let c = 3;
12744    }"
12745        .unindent(),
12746        cx,
12747    )
12748    .await;
12749
12750    assert_indent_guides(
12751        0..5,
12752        vec![
12753            indent_guide(buffer_id, 1, 3, 0),
12754            indent_guide(buffer_id, 2, 2, 1),
12755        ],
12756        None,
12757        &mut cx,
12758    );
12759}
12760
12761#[gpui::test]
12762async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12763    let (buffer_id, mut cx) = setup_indent_guides_editor(
12764        &"
12765        fn main() {
12766            let a = 1;
12767
12768            let c = 3;
12769        }"
12770        .unindent(),
12771        cx,
12772    )
12773    .await;
12774
12775    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12776}
12777
12778#[gpui::test]
12779async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12780    let (buffer_id, mut cx) = setup_indent_guides_editor(
12781        &"
12782        fn main() {
12783            let a = 1;
12784
12785            let c = 3;
12786
12787            if a == 3 {
12788                let b = 2;
12789            } else {
12790                let c = 3;
12791            }
12792        }"
12793        .unindent(),
12794        cx,
12795    )
12796    .await;
12797
12798    assert_indent_guides(
12799        0..11,
12800        vec![
12801            indent_guide(buffer_id, 1, 9, 0),
12802            indent_guide(buffer_id, 6, 6, 1),
12803            indent_guide(buffer_id, 8, 8, 1),
12804        ],
12805        None,
12806        &mut cx,
12807    );
12808}
12809
12810#[gpui::test]
12811async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12812    let (buffer_id, mut cx) = setup_indent_guides_editor(
12813        &"
12814        fn main() {
12815            let a = 1;
12816
12817            let c = 3;
12818
12819            if a == 3 {
12820                let b = 2;
12821            } else {
12822                let c = 3;
12823            }
12824        }"
12825        .unindent(),
12826        cx,
12827    )
12828    .await;
12829
12830    assert_indent_guides(
12831        1..11,
12832        vec![
12833            indent_guide(buffer_id, 1, 9, 0),
12834            indent_guide(buffer_id, 6, 6, 1),
12835            indent_guide(buffer_id, 8, 8, 1),
12836        ],
12837        None,
12838        &mut cx,
12839    );
12840}
12841
12842#[gpui::test]
12843async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12844    let (buffer_id, mut cx) = setup_indent_guides_editor(
12845        &"
12846        fn main() {
12847            let a = 1;
12848
12849            let c = 3;
12850
12851            if a == 3 {
12852                let b = 2;
12853            } else {
12854                let c = 3;
12855            }
12856        }"
12857        .unindent(),
12858        cx,
12859    )
12860    .await;
12861
12862    assert_indent_guides(
12863        1..10,
12864        vec![
12865            indent_guide(buffer_id, 1, 9, 0),
12866            indent_guide(buffer_id, 6, 6, 1),
12867            indent_guide(buffer_id, 8, 8, 1),
12868        ],
12869        None,
12870        &mut cx,
12871    );
12872}
12873
12874#[gpui::test]
12875async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12876    let (buffer_id, mut cx) = setup_indent_guides_editor(
12877        &"
12878        block1
12879            block2
12880                block3
12881                    block4
12882            block2
12883        block1
12884        block1"
12885            .unindent(),
12886        cx,
12887    )
12888    .await;
12889
12890    assert_indent_guides(
12891        1..10,
12892        vec![
12893            indent_guide(buffer_id, 1, 4, 0),
12894            indent_guide(buffer_id, 2, 3, 1),
12895            indent_guide(buffer_id, 3, 3, 2),
12896        ],
12897        None,
12898        &mut cx,
12899    );
12900}
12901
12902#[gpui::test]
12903async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12904    let (buffer_id, mut cx) = setup_indent_guides_editor(
12905        &"
12906        block1
12907            block2
12908                block3
12909
12910        block1
12911        block1"
12912            .unindent(),
12913        cx,
12914    )
12915    .await;
12916
12917    assert_indent_guides(
12918        0..6,
12919        vec![
12920            indent_guide(buffer_id, 1, 2, 0),
12921            indent_guide(buffer_id, 2, 2, 1),
12922        ],
12923        None,
12924        &mut cx,
12925    );
12926}
12927
12928#[gpui::test]
12929async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12930    let (buffer_id, mut cx) = setup_indent_guides_editor(
12931        &"
12932        block1
12933
12934
12935
12936            block2
12937        "
12938        .unindent(),
12939        cx,
12940    )
12941    .await;
12942
12943    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12944}
12945
12946#[gpui::test]
12947async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12948    let (buffer_id, mut cx) = setup_indent_guides_editor(
12949        &"
12950        def a:
12951        \tb = 3
12952        \tif True:
12953        \t\tc = 4
12954        \t\td = 5
12955        \tprint(b)
12956        "
12957        .unindent(),
12958        cx,
12959    )
12960    .await;
12961
12962    assert_indent_guides(
12963        0..6,
12964        vec![
12965            indent_guide(buffer_id, 1, 6, 0),
12966            indent_guide(buffer_id, 3, 4, 1),
12967        ],
12968        None,
12969        &mut cx,
12970    );
12971}
12972
12973#[gpui::test]
12974async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12975    let (buffer_id, mut cx) = setup_indent_guides_editor(
12976        &"
12977    fn main() {
12978        let a = 1;
12979    }"
12980        .unindent(),
12981        cx,
12982    )
12983    .await;
12984
12985    cx.update_editor(|editor, cx| {
12986        editor.change_selections(None, cx, |s| {
12987            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12988        });
12989    });
12990
12991    assert_indent_guides(
12992        0..3,
12993        vec![indent_guide(buffer_id, 1, 1, 0)],
12994        Some(vec![0]),
12995        &mut cx,
12996    );
12997}
12998
12999#[gpui::test]
13000async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13001    let (buffer_id, mut cx) = setup_indent_guides_editor(
13002        &"
13003    fn main() {
13004        if 1 == 2 {
13005            let a = 1;
13006        }
13007    }"
13008        .unindent(),
13009        cx,
13010    )
13011    .await;
13012
13013    cx.update_editor(|editor, cx| {
13014        editor.change_selections(None, cx, |s| {
13015            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13016        });
13017    });
13018
13019    assert_indent_guides(
13020        0..4,
13021        vec![
13022            indent_guide(buffer_id, 1, 3, 0),
13023            indent_guide(buffer_id, 2, 2, 1),
13024        ],
13025        Some(vec![1]),
13026        &mut cx,
13027    );
13028
13029    cx.update_editor(|editor, cx| {
13030        editor.change_selections(None, cx, |s| {
13031            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13032        });
13033    });
13034
13035    assert_indent_guides(
13036        0..4,
13037        vec![
13038            indent_guide(buffer_id, 1, 3, 0),
13039            indent_guide(buffer_id, 2, 2, 1),
13040        ],
13041        Some(vec![1]),
13042        &mut cx,
13043    );
13044
13045    cx.update_editor(|editor, cx| {
13046        editor.change_selections(None, cx, |s| {
13047            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13048        });
13049    });
13050
13051    assert_indent_guides(
13052        0..4,
13053        vec![
13054            indent_guide(buffer_id, 1, 3, 0),
13055            indent_guide(buffer_id, 2, 2, 1),
13056        ],
13057        Some(vec![0]),
13058        &mut cx,
13059    );
13060}
13061
13062#[gpui::test]
13063async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13064    let (buffer_id, mut cx) = setup_indent_guides_editor(
13065        &"
13066    fn main() {
13067        let a = 1;
13068
13069        let b = 2;
13070    }"
13071        .unindent(),
13072        cx,
13073    )
13074    .await;
13075
13076    cx.update_editor(|editor, cx| {
13077        editor.change_selections(None, cx, |s| {
13078            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13079        });
13080    });
13081
13082    assert_indent_guides(
13083        0..5,
13084        vec![indent_guide(buffer_id, 1, 3, 0)],
13085        Some(vec![0]),
13086        &mut cx,
13087    );
13088}
13089
13090#[gpui::test]
13091async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13092    let (buffer_id, mut cx) = setup_indent_guides_editor(
13093        &"
13094    def m:
13095        a = 1
13096        pass"
13097            .unindent(),
13098        cx,
13099    )
13100    .await;
13101
13102    cx.update_editor(|editor, cx| {
13103        editor.change_selections(None, cx, |s| {
13104            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13105        });
13106    });
13107
13108    assert_indent_guides(
13109        0..3,
13110        vec![indent_guide(buffer_id, 1, 2, 0)],
13111        Some(vec![0]),
13112        &mut cx,
13113    );
13114}
13115
13116#[gpui::test]
13117fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13118    init_test(cx, |_| {});
13119
13120    let editor = cx.add_window(|cx| {
13121        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13122        build_editor(buffer, cx)
13123    });
13124
13125    let render_args = Arc::new(Mutex::new(None));
13126    let snapshot = editor
13127        .update(cx, |editor, cx| {
13128            let snapshot = editor.buffer().read(cx).snapshot(cx);
13129            let range =
13130                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13131
13132            struct RenderArgs {
13133                row: MultiBufferRow,
13134                folded: bool,
13135                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13136            }
13137
13138            let crease = Crease::new(
13139                range,
13140                FoldPlaceholder::test(),
13141                {
13142                    let toggle_callback = render_args.clone();
13143                    move |row, folded, callback, _cx| {
13144                        *toggle_callback.lock() = Some(RenderArgs {
13145                            row,
13146                            folded,
13147                            callback,
13148                        });
13149                        div()
13150                    }
13151                },
13152                |_row, _folded, _cx| div(),
13153            );
13154
13155            editor.insert_creases(Some(crease), cx);
13156            let snapshot = editor.snapshot(cx);
13157            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13158            snapshot
13159        })
13160        .unwrap();
13161
13162    let render_args = render_args.lock().take().unwrap();
13163    assert_eq!(render_args.row, MultiBufferRow(1));
13164    assert_eq!(render_args.folded, false);
13165    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13166
13167    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13168        .unwrap();
13169    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13170    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13171
13172    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13173        .unwrap();
13174    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13175    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13176}
13177
13178#[gpui::test]
13179async fn test_input_text(cx: &mut gpui::TestAppContext) {
13180    init_test(cx, |_| {});
13181    let mut cx = EditorTestContext::new(cx).await;
13182
13183    cx.set_state(
13184        &r#"ˇone
13185        two
13186
13187        three
13188        fourˇ
13189        five
13190
13191        siˇx"#
13192            .unindent(),
13193    );
13194
13195    cx.dispatch_action(HandleInput(String::new()));
13196    cx.assert_editor_state(
13197        &r#"ˇone
13198        two
13199
13200        three
13201        fourˇ
13202        five
13203
13204        siˇx"#
13205            .unindent(),
13206    );
13207
13208    cx.dispatch_action(HandleInput("AAAA".to_string()));
13209    cx.assert_editor_state(
13210        &r#"AAAAˇone
13211        two
13212
13213        three
13214        fourAAAAˇ
13215        five
13216
13217        siAAAAˇx"#
13218            .unindent(),
13219    );
13220}
13221
13222#[gpui::test]
13223async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13224    init_test(cx, |_| {});
13225
13226    let mut cx = EditorTestContext::new(cx).await;
13227    cx.set_state(
13228        r#"let foo = 1;
13229let foo = 2;
13230let foo = 3;
13231let fooˇ = 4;
13232let foo = 5;
13233let foo = 6;
13234let foo = 7;
13235let foo = 8;
13236let foo = 9;
13237let foo = 10;
13238let foo = 11;
13239let foo = 12;
13240let foo = 13;
13241let foo = 14;
13242let foo = 15;"#,
13243    );
13244
13245    cx.update_editor(|e, cx| {
13246        assert_eq!(
13247            e.next_scroll_position,
13248            NextScrollCursorCenterTopBottom::Center,
13249            "Default next scroll direction is center",
13250        );
13251
13252        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13253        assert_eq!(
13254            e.next_scroll_position,
13255            NextScrollCursorCenterTopBottom::Top,
13256            "After center, next scroll direction should be top",
13257        );
13258
13259        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13260        assert_eq!(
13261            e.next_scroll_position,
13262            NextScrollCursorCenterTopBottom::Bottom,
13263            "After top, next scroll direction should be bottom",
13264        );
13265
13266        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13267        assert_eq!(
13268            e.next_scroll_position,
13269            NextScrollCursorCenterTopBottom::Center,
13270            "After bottom, scrolling should start over",
13271        );
13272
13273        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13274        assert_eq!(
13275            e.next_scroll_position,
13276            NextScrollCursorCenterTopBottom::Top,
13277            "Scrolling continues if retriggered fast enough"
13278        );
13279    });
13280
13281    cx.executor()
13282        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13283    cx.executor().run_until_parked();
13284    cx.update_editor(|e, _| {
13285        assert_eq!(
13286            e.next_scroll_position,
13287            NextScrollCursorCenterTopBottom::Center,
13288            "If scrolling is not triggered fast enough, it should reset"
13289        );
13290    });
13291}
13292
13293#[gpui::test]
13294async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13295    init_test(cx, |_| {});
13296    let mut cx = EditorLspTestContext::new_rust(
13297        lsp::ServerCapabilities {
13298            definition_provider: Some(lsp::OneOf::Left(true)),
13299            references_provider: Some(lsp::OneOf::Left(true)),
13300            ..lsp::ServerCapabilities::default()
13301        },
13302        cx,
13303    )
13304    .await;
13305
13306    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13307        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13308            move |params, _| async move {
13309                if empty_go_to_definition {
13310                    Ok(None)
13311                } else {
13312                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13313                        uri: params.text_document_position_params.text_document.uri,
13314                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13315                    })))
13316                }
13317            },
13318        );
13319        let references =
13320            cx.lsp
13321                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13322                    Ok(Some(vec![lsp::Location {
13323                        uri: params.text_document_position.text_document.uri,
13324                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13325                    }]))
13326                });
13327        (go_to_definition, references)
13328    };
13329
13330    cx.set_state(
13331        &r#"fn one() {
13332            let mut a = ˇtwo();
13333        }
13334
13335        fn two() {}"#
13336            .unindent(),
13337    );
13338    set_up_lsp_handlers(false, &mut cx);
13339    let navigated = cx
13340        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13341        .await
13342        .expect("Failed to navigate to definition");
13343    assert_eq!(
13344        navigated,
13345        Navigated::Yes,
13346        "Should have navigated to definition from the GetDefinition response"
13347    );
13348    cx.assert_editor_state(
13349        &r#"fn one() {
13350            let mut a = two();
13351        }
13352
13353        fn «twoˇ»() {}"#
13354            .unindent(),
13355    );
13356
13357    let editors = cx.update_workspace(|workspace, cx| {
13358        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13359    });
13360    cx.update_editor(|_, test_editor_cx| {
13361        assert_eq!(
13362            editors.len(),
13363            1,
13364            "Initially, only one, test, editor should be open in the workspace"
13365        );
13366        assert_eq!(
13367            test_editor_cx.view(),
13368            editors.last().expect("Asserted len is 1")
13369        );
13370    });
13371
13372    set_up_lsp_handlers(true, &mut cx);
13373    let navigated = cx
13374        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13375        .await
13376        .expect("Failed to navigate to lookup references");
13377    assert_eq!(
13378        navigated,
13379        Navigated::Yes,
13380        "Should have navigated to references as a fallback after empty GoToDefinition response"
13381    );
13382    // We should not change the selections in the existing file,
13383    // if opening another milti buffer with the references
13384    cx.assert_editor_state(
13385        &r#"fn one() {
13386            let mut a = two();
13387        }
13388
13389        fn «twoˇ»() {}"#
13390            .unindent(),
13391    );
13392    let editors = cx.update_workspace(|workspace, cx| {
13393        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13394    });
13395    cx.update_editor(|_, test_editor_cx| {
13396        assert_eq!(
13397            editors.len(),
13398            2,
13399            "After falling back to references search, we open a new editor with the results"
13400        );
13401        let references_fallback_text = editors
13402            .into_iter()
13403            .find(|new_editor| new_editor != test_editor_cx.view())
13404            .expect("Should have one non-test editor now")
13405            .read(test_editor_cx)
13406            .text(test_editor_cx);
13407        assert_eq!(
13408            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13409            "Should use the range from the references response and not the GoToDefinition one"
13410        );
13411    });
13412}
13413
13414fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13415    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13416    point..point
13417}
13418
13419fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13420    let (text, ranges) = marked_text_ranges(marked_text, true);
13421    assert_eq!(view.text(cx), text);
13422    assert_eq!(
13423        view.selections.ranges(cx),
13424        ranges,
13425        "Assert selections are {}",
13426        marked_text
13427    );
13428}
13429
13430pub fn handle_signature_help_request(
13431    cx: &mut EditorLspTestContext,
13432    mocked_response: lsp::SignatureHelp,
13433) -> impl Future<Output = ()> {
13434    let mut request =
13435        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13436            let mocked_response = mocked_response.clone();
13437            async move { Ok(Some(mocked_response)) }
13438        });
13439
13440    async move {
13441        request.next().await;
13442    }
13443}
13444
13445/// Handle completion request passing a marked string specifying where the completion
13446/// should be triggered from using '|' character, what range should be replaced, and what completions
13447/// should be returned using '<' and '>' to delimit the range
13448pub fn handle_completion_request(
13449    cx: &mut EditorLspTestContext,
13450    marked_string: &str,
13451    completions: Vec<&'static str>,
13452    counter: Arc<AtomicUsize>,
13453) -> impl Future<Output = ()> {
13454    let complete_from_marker: TextRangeMarker = '|'.into();
13455    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13456    let (_, mut marked_ranges) = marked_text_ranges_by(
13457        marked_string,
13458        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13459    );
13460
13461    let complete_from_position =
13462        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13463    let replace_range =
13464        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13465
13466    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13467        let completions = completions.clone();
13468        counter.fetch_add(1, atomic::Ordering::Release);
13469        async move {
13470            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13471            assert_eq!(
13472                params.text_document_position.position,
13473                complete_from_position
13474            );
13475            Ok(Some(lsp::CompletionResponse::Array(
13476                completions
13477                    .iter()
13478                    .map(|completion_text| lsp::CompletionItem {
13479                        label: completion_text.to_string(),
13480                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13481                            range: replace_range,
13482                            new_text: completion_text.to_string(),
13483                        })),
13484                        ..Default::default()
13485                    })
13486                    .collect(),
13487            )))
13488        }
13489    });
13490
13491    async move {
13492        request.next().await;
13493    }
13494}
13495
13496fn handle_resolve_completion_request(
13497    cx: &mut EditorLspTestContext,
13498    edits: Option<Vec<(&'static str, &'static str)>>,
13499) -> impl Future<Output = ()> {
13500    let edits = edits.map(|edits| {
13501        edits
13502            .iter()
13503            .map(|(marked_string, new_text)| {
13504                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13505                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13506                lsp::TextEdit::new(replace_range, new_text.to_string())
13507            })
13508            .collect::<Vec<_>>()
13509    });
13510
13511    let mut request =
13512        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13513            let edits = edits.clone();
13514            async move {
13515                Ok(lsp::CompletionItem {
13516                    additional_text_edits: edits,
13517                    ..Default::default()
13518                })
13519            }
13520        });
13521
13522    async move {
13523        request.next().await;
13524    }
13525}
13526
13527pub(crate) fn update_test_language_settings(
13528    cx: &mut TestAppContext,
13529    f: impl Fn(&mut AllLanguageSettingsContent),
13530) {
13531    _ = cx.update(|cx| {
13532        SettingsStore::update_global(cx, |store, cx| {
13533            store.update_user_settings::<AllLanguageSettings>(cx, f);
13534        });
13535    });
13536}
13537
13538pub(crate) fn update_test_project_settings(
13539    cx: &mut TestAppContext,
13540    f: impl Fn(&mut ProjectSettings),
13541) {
13542    _ = cx.update(|cx| {
13543        SettingsStore::update_global(cx, |store, cx| {
13544            store.update_user_settings::<ProjectSettings>(cx, f);
13545        });
13546    });
13547}
13548
13549pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13550    _ = cx.update(|cx| {
13551        assets::Assets.load_test_fonts(cx);
13552        let store = SettingsStore::test(cx);
13553        cx.set_global(store);
13554        theme::init(theme::LoadThemes::JustBase, cx);
13555        release_channel::init(SemanticVersion::default(), cx);
13556        client::init_settings(cx);
13557        language::init(cx);
13558        Project::init_settings(cx);
13559        workspace::init_settings(cx);
13560        crate::init(cx);
13561    });
13562
13563    update_test_language_settings(cx, f);
13564}
13565
13566pub(crate) fn rust_lang() -> Arc<Language> {
13567    Arc::new(Language::new(
13568        LanguageConfig {
13569            name: "Rust".into(),
13570            matcher: LanguageMatcher {
13571                path_suffixes: vec!["rs".to_string()],
13572                ..Default::default()
13573            },
13574            ..Default::default()
13575        },
13576        Some(tree_sitter_rust::language()),
13577    ))
13578}
13579
13580#[track_caller]
13581fn assert_hunk_revert(
13582    not_reverted_text_with_selections: &str,
13583    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13584    expected_reverted_text_with_selections: &str,
13585    base_text: &str,
13586    cx: &mut EditorLspTestContext,
13587) {
13588    cx.set_state(not_reverted_text_with_selections);
13589    cx.update_editor(|editor, cx| {
13590        editor
13591            .buffer()
13592            .read(cx)
13593            .as_singleton()
13594            .unwrap()
13595            .update(cx, |buffer, cx| {
13596                buffer.set_diff_base(Some(base_text.into()), cx);
13597            });
13598    });
13599    cx.executor().run_until_parked();
13600
13601    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13602        let snapshot = editor.buffer().read(cx).snapshot(cx);
13603        let reverted_hunk_statuses = snapshot
13604            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13605            .map(|hunk| hunk_status(&hunk))
13606            .collect::<Vec<_>>();
13607
13608        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13609        reverted_hunk_statuses
13610    });
13611    cx.executor().run_until_parked();
13612    cx.assert_editor_state(expected_reverted_text_with_selections);
13613    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13614}