editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   24    LanguageName, Override, ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(
 2106            &DeleteToPreviousWordStart {
 2107                ignore_newlines: false,
 2108            },
 2109            cx,
 2110        );
 2111        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2112    });
 2113
 2114    _ = view.update(cx, |view, cx| {
 2115        view.change_selections(None, cx, |s| {
 2116            s.select_display_ranges([
 2117                // an empty selection - the following word fragment is deleted
 2118                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2119                // characters selected - they are deleted
 2120                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2121            ])
 2122        });
 2123        view.delete_to_next_word_end(
 2124            &DeleteToNextWordEnd {
 2125                ignore_newlines: false,
 2126            },
 2127            cx,
 2128        );
 2129        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2130    });
 2131}
 2132
 2133#[gpui::test]
 2134fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136
 2137    let view = cx.add_window(|cx| {
 2138        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2139        build_editor(buffer.clone(), cx)
 2140    });
 2141    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2142        ignore_newlines: false,
 2143    };
 2144    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2145        ignore_newlines: true,
 2146    };
 2147
 2148    _ = view.update(cx, |view, cx| {
 2149        view.change_selections(None, cx, |s| {
 2150            s.select_display_ranges([
 2151                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2152            ])
 2153        });
 2154        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2155        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2156        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2157        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2158        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2159        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2160        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2161        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2162        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2163        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2164        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2165        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2166    });
 2167}
 2168
 2169#[gpui::test]
 2170fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2171    init_test(cx, |_| {});
 2172
 2173    let view = cx.add_window(|cx| {
 2174        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2175        build_editor(buffer.clone(), cx)
 2176    });
 2177    let del_to_next_word_end = DeleteToNextWordEnd {
 2178        ignore_newlines: false,
 2179    };
 2180    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2181        ignore_newlines: true,
 2182    };
 2183
 2184    _ = view.update(cx, |view, cx| {
 2185        view.change_selections(None, cx, |s| {
 2186            s.select_display_ranges([
 2187                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2188            ])
 2189        });
 2190        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2191        assert_eq!(
 2192            view.buffer.read(cx).read(cx).text(),
 2193            "one\n   two\nthree\n   four"
 2194        );
 2195        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2196        assert_eq!(
 2197            view.buffer.read(cx).read(cx).text(),
 2198            "\n   two\nthree\n   four"
 2199        );
 2200        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2201        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2202        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2203        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2204        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2205        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2206        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2207        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2208    });
 2209}
 2210
 2211#[gpui::test]
 2212fn test_newline(cx: &mut TestAppContext) {
 2213    init_test(cx, |_| {});
 2214
 2215    let view = cx.add_window(|cx| {
 2216        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2217        build_editor(buffer.clone(), cx)
 2218    });
 2219
 2220    _ = view.update(cx, |view, cx| {
 2221        view.change_selections(None, cx, |s| {
 2222            s.select_display_ranges([
 2223                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2224                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2225                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2226            ])
 2227        });
 2228
 2229        view.newline(&Newline, cx);
 2230        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2231    });
 2232}
 2233
 2234#[gpui::test]
 2235fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2236    init_test(cx, |_| {});
 2237
 2238    let editor = cx.add_window(|cx| {
 2239        let buffer = MultiBuffer::build_simple(
 2240            "
 2241                a
 2242                b(
 2243                    X
 2244                )
 2245                c(
 2246                    X
 2247                )
 2248            "
 2249            .unindent()
 2250            .as_str(),
 2251            cx,
 2252        );
 2253        let mut editor = build_editor(buffer.clone(), cx);
 2254        editor.change_selections(None, cx, |s| {
 2255            s.select_ranges([
 2256                Point::new(2, 4)..Point::new(2, 5),
 2257                Point::new(5, 4)..Point::new(5, 5),
 2258            ])
 2259        });
 2260        editor
 2261    });
 2262
 2263    _ = editor.update(cx, |editor, cx| {
 2264        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2265        editor.buffer.update(cx, |buffer, cx| {
 2266            buffer.edit(
 2267                [
 2268                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2269                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2270                ],
 2271                None,
 2272                cx,
 2273            );
 2274            assert_eq!(
 2275                buffer.read(cx).text(),
 2276                "
 2277                    a
 2278                    b()
 2279                    c()
 2280                "
 2281                .unindent()
 2282            );
 2283        });
 2284        assert_eq!(
 2285            editor.selections.ranges(cx),
 2286            &[
 2287                Point::new(1, 2)..Point::new(1, 2),
 2288                Point::new(2, 2)..Point::new(2, 2),
 2289            ],
 2290        );
 2291
 2292        editor.newline(&Newline, cx);
 2293        assert_eq!(
 2294            editor.text(cx),
 2295            "
 2296                a
 2297                b(
 2298                )
 2299                c(
 2300                )
 2301            "
 2302            .unindent()
 2303        );
 2304
 2305        // The selections are moved after the inserted newlines
 2306        assert_eq!(
 2307            editor.selections.ranges(cx),
 2308            &[
 2309                Point::new(2, 0)..Point::new(2, 0),
 2310                Point::new(4, 0)..Point::new(4, 0),
 2311            ],
 2312        );
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2318    init_test(cx, |settings| {
 2319        settings.defaults.tab_size = NonZeroU32::new(4)
 2320    });
 2321
 2322    let language = Arc::new(
 2323        Language::new(
 2324            LanguageConfig::default(),
 2325            Some(tree_sitter_rust::LANGUAGE.into()),
 2326        )
 2327        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2328        .unwrap(),
 2329    );
 2330
 2331    let mut cx = EditorTestContext::new(cx).await;
 2332    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2333    cx.set_state(indoc! {"
 2334        const a: ˇA = (
 2335 2336                «const_functionˇ»(ˇ),
 2337                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2338 2339        ˇ);ˇ
 2340    "});
 2341
 2342    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2343    cx.assert_editor_state(indoc! {"
 2344        ˇ
 2345        const a: A = (
 2346            ˇ
 2347            (
 2348                ˇ
 2349                ˇ
 2350                const_function(),
 2351                ˇ
 2352                ˇ
 2353                ˇ
 2354                ˇ
 2355                something_else,
 2356                ˇ
 2357            )
 2358            ˇ
 2359            ˇ
 2360        );
 2361    "});
 2362}
 2363
 2364#[gpui::test]
 2365async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2366    init_test(cx, |settings| {
 2367        settings.defaults.tab_size = NonZeroU32::new(4)
 2368    });
 2369
 2370    let language = Arc::new(
 2371        Language::new(
 2372            LanguageConfig::default(),
 2373            Some(tree_sitter_rust::LANGUAGE.into()),
 2374        )
 2375        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2376        .unwrap(),
 2377    );
 2378
 2379    let mut cx = EditorTestContext::new(cx).await;
 2380    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2381    cx.set_state(indoc! {"
 2382        const a: ˇA = (
 2383 2384                «const_functionˇ»(ˇ),
 2385                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2386 2387        ˇ);ˇ
 2388    "});
 2389
 2390    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2391    cx.assert_editor_state(indoc! {"
 2392        const a: A = (
 2393            ˇ
 2394            (
 2395                ˇ
 2396                const_function(),
 2397                ˇ
 2398                ˇ
 2399                something_else,
 2400                ˇ
 2401                ˇ
 2402                ˇ
 2403                ˇ
 2404            )
 2405            ˇ
 2406        );
 2407        ˇ
 2408        ˇ
 2409    "});
 2410}
 2411
 2412#[gpui::test]
 2413async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2414    init_test(cx, |settings| {
 2415        settings.defaults.tab_size = NonZeroU32::new(4)
 2416    });
 2417
 2418    let language = Arc::new(Language::new(
 2419        LanguageConfig {
 2420            line_comments: vec!["//".into()],
 2421            ..LanguageConfig::default()
 2422        },
 2423        None,
 2424    ));
 2425    {
 2426        let mut cx = EditorTestContext::new(cx).await;
 2427        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2428        cx.set_state(indoc! {"
 2429        // Fooˇ
 2430    "});
 2431
 2432        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2433        cx.assert_editor_state(indoc! {"
 2434        // Foo
 2435        //ˇ
 2436    "});
 2437        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2438        cx.set_state(indoc! {"
 2439        ˇ// Foo
 2440    "});
 2441        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2442        cx.assert_editor_state(indoc! {"
 2443
 2444        ˇ// Foo
 2445    "});
 2446    }
 2447    // Ensure that comment continuations can be disabled.
 2448    update_test_language_settings(cx, |settings| {
 2449        settings.defaults.extend_comment_on_newline = Some(false);
 2450    });
 2451    let mut cx = EditorTestContext::new(cx).await;
 2452    cx.set_state(indoc! {"
 2453        // Fooˇ
 2454    "});
 2455    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2456    cx.assert_editor_state(indoc! {"
 2457        // Foo
 2458        ˇ
 2459    "});
 2460}
 2461
 2462#[gpui::test]
 2463fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2464    init_test(cx, |_| {});
 2465
 2466    let editor = cx.add_window(|cx| {
 2467        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2468        let mut editor = build_editor(buffer.clone(), cx);
 2469        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2470        editor
 2471    });
 2472
 2473    _ = editor.update(cx, |editor, cx| {
 2474        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2475        editor.buffer.update(cx, |buffer, cx| {
 2476            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2477            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2478        });
 2479        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2480
 2481        editor.insert("Z", cx);
 2482        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2483
 2484        // The selections are moved after the inserted characters
 2485        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2486    });
 2487}
 2488
 2489#[gpui::test]
 2490async fn test_tab(cx: &mut gpui::TestAppContext) {
 2491    init_test(cx, |settings| {
 2492        settings.defaults.tab_size = NonZeroU32::new(3)
 2493    });
 2494
 2495    let mut cx = EditorTestContext::new(cx).await;
 2496    cx.set_state(indoc! {"
 2497        ˇabˇc
 2498        ˇ🏀ˇ🏀ˇefg
 2499 2500    "});
 2501    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2502    cx.assert_editor_state(indoc! {"
 2503           ˇab ˇc
 2504           ˇ🏀  ˇ🏀  ˇefg
 2505        d  ˇ
 2506    "});
 2507
 2508    cx.set_state(indoc! {"
 2509        a
 2510        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2511    "});
 2512    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2513    cx.assert_editor_state(indoc! {"
 2514        a
 2515           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2516    "});
 2517}
 2518
 2519#[gpui::test]
 2520async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2521    init_test(cx, |_| {});
 2522
 2523    let mut cx = EditorTestContext::new(cx).await;
 2524    let language = Arc::new(
 2525        Language::new(
 2526            LanguageConfig::default(),
 2527            Some(tree_sitter_rust::LANGUAGE.into()),
 2528        )
 2529        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2530        .unwrap(),
 2531    );
 2532    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533
 2534    // cursors that are already at the suggested indent level insert
 2535    // a soft tab. cursors that are to the left of the suggested indent
 2536    // auto-indent their line.
 2537    cx.set_state(indoc! {"
 2538        ˇ
 2539        const a: B = (
 2540            c(
 2541                d(
 2542        ˇ
 2543                )
 2544        ˇ
 2545        ˇ    )
 2546        );
 2547    "});
 2548    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2549    cx.assert_editor_state(indoc! {"
 2550            ˇ
 2551        const a: B = (
 2552            c(
 2553                d(
 2554                    ˇ
 2555                )
 2556                ˇ
 2557            ˇ)
 2558        );
 2559    "});
 2560
 2561    // handle auto-indent when there are multiple cursors on the same line
 2562    cx.set_state(indoc! {"
 2563        const a: B = (
 2564            c(
 2565        ˇ    ˇ
 2566        ˇ    )
 2567        );
 2568    "});
 2569    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2570    cx.assert_editor_state(indoc! {"
 2571        const a: B = (
 2572            c(
 2573                ˇ
 2574            ˇ)
 2575        );
 2576    "});
 2577}
 2578
 2579#[gpui::test]
 2580async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2581    init_test(cx, |settings| {
 2582        settings.defaults.tab_size = NonZeroU32::new(4)
 2583    });
 2584
 2585    let language = Arc::new(
 2586        Language::new(
 2587            LanguageConfig::default(),
 2588            Some(tree_sitter_rust::LANGUAGE.into()),
 2589        )
 2590        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2591        .unwrap(),
 2592    );
 2593
 2594    let mut cx = EditorTestContext::new(cx).await;
 2595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2596    cx.set_state(indoc! {"
 2597        fn a() {
 2598            if b {
 2599        \t ˇc
 2600            }
 2601        }
 2602    "});
 2603
 2604    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2605    cx.assert_editor_state(indoc! {"
 2606        fn a() {
 2607            if b {
 2608                ˇc
 2609            }
 2610        }
 2611    "});
 2612}
 2613
 2614#[gpui::test]
 2615async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2616    init_test(cx, |settings| {
 2617        settings.defaults.tab_size = NonZeroU32::new(4);
 2618    });
 2619
 2620    let mut cx = EditorTestContext::new(cx).await;
 2621
 2622    cx.set_state(indoc! {"
 2623          «oneˇ» «twoˇ»
 2624        three
 2625         four
 2626    "});
 2627    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2628    cx.assert_editor_state(indoc! {"
 2629            «oneˇ» «twoˇ»
 2630        three
 2631         four
 2632    "});
 2633
 2634    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2635    cx.assert_editor_state(indoc! {"
 2636        «oneˇ» «twoˇ»
 2637        three
 2638         four
 2639    "});
 2640
 2641    // select across line ending
 2642    cx.set_state(indoc! {"
 2643        one two
 2644        t«hree
 2645        ˇ» four
 2646    "});
 2647    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2648    cx.assert_editor_state(indoc! {"
 2649        one two
 2650            t«hree
 2651        ˇ» four
 2652    "});
 2653
 2654    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2655    cx.assert_editor_state(indoc! {"
 2656        one two
 2657        t«hree
 2658        ˇ» four
 2659    "});
 2660
 2661    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2662    cx.set_state(indoc! {"
 2663        one two
 2664        ˇthree
 2665            four
 2666    "});
 2667    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2668    cx.assert_editor_state(indoc! {"
 2669        one two
 2670            ˇthree
 2671            four
 2672    "});
 2673
 2674    cx.set_state(indoc! {"
 2675        one two
 2676        ˇ    three
 2677            four
 2678    "});
 2679    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        one two
 2682        ˇthree
 2683            four
 2684    "});
 2685}
 2686
 2687#[gpui::test]
 2688async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2689    init_test(cx, |settings| {
 2690        settings.defaults.hard_tabs = Some(true);
 2691    });
 2692
 2693    let mut cx = EditorTestContext::new(cx).await;
 2694
 2695    // select two ranges on one line
 2696    cx.set_state(indoc! {"
 2697        «oneˇ» «twoˇ»
 2698        three
 2699        four
 2700    "});
 2701    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2702    cx.assert_editor_state(indoc! {"
 2703        \t«oneˇ» «twoˇ»
 2704        three
 2705        four
 2706    "});
 2707    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2708    cx.assert_editor_state(indoc! {"
 2709        \t\t«oneˇ» «twoˇ»
 2710        three
 2711        four
 2712    "});
 2713    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2714    cx.assert_editor_state(indoc! {"
 2715        \t«oneˇ» «twoˇ»
 2716        three
 2717        four
 2718    "});
 2719    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2720    cx.assert_editor_state(indoc! {"
 2721        «oneˇ» «twoˇ»
 2722        three
 2723        four
 2724    "});
 2725
 2726    // select across a line ending
 2727    cx.set_state(indoc! {"
 2728        one two
 2729        t«hree
 2730        ˇ»four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734        one two
 2735        \tt«hree
 2736        ˇ»four
 2737    "});
 2738    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2739    cx.assert_editor_state(indoc! {"
 2740        one two
 2741        \t\tt«hree
 2742        ˇ»four
 2743    "});
 2744    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2745    cx.assert_editor_state(indoc! {"
 2746        one two
 2747        \tt«hree
 2748        ˇ»four
 2749    "});
 2750    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2751    cx.assert_editor_state(indoc! {"
 2752        one two
 2753        t«hree
 2754        ˇ»four
 2755    "});
 2756
 2757    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2758    cx.set_state(indoc! {"
 2759        one two
 2760        ˇthree
 2761        four
 2762    "});
 2763    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2764    cx.assert_editor_state(indoc! {"
 2765        one two
 2766        ˇthree
 2767        four
 2768    "});
 2769    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2770    cx.assert_editor_state(indoc! {"
 2771        one two
 2772        \tˇthree
 2773        four
 2774    "});
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        one two
 2778        ˇthree
 2779        four
 2780    "});
 2781}
 2782
 2783#[gpui::test]
 2784fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2785    init_test(cx, |settings| {
 2786        settings.languages.extend([
 2787            (
 2788                "TOML".into(),
 2789                LanguageSettingsContent {
 2790                    tab_size: NonZeroU32::new(2),
 2791                    ..Default::default()
 2792                },
 2793            ),
 2794            (
 2795                "Rust".into(),
 2796                LanguageSettingsContent {
 2797                    tab_size: NonZeroU32::new(4),
 2798                    ..Default::default()
 2799                },
 2800            ),
 2801        ]);
 2802    });
 2803
 2804    let toml_language = Arc::new(Language::new(
 2805        LanguageConfig {
 2806            name: "TOML".into(),
 2807            ..Default::default()
 2808        },
 2809        None,
 2810    ));
 2811    let rust_language = Arc::new(Language::new(
 2812        LanguageConfig {
 2813            name: "Rust".into(),
 2814            ..Default::default()
 2815        },
 2816        None,
 2817    ));
 2818
 2819    let toml_buffer =
 2820        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2821    let rust_buffer = cx.new_model(|cx| {
 2822        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2823    });
 2824    let multibuffer = cx.new_model(|cx| {
 2825        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2826        multibuffer.push_excerpts(
 2827            toml_buffer.clone(),
 2828            [ExcerptRange {
 2829                context: Point::new(0, 0)..Point::new(2, 0),
 2830                primary: None,
 2831            }],
 2832            cx,
 2833        );
 2834        multibuffer.push_excerpts(
 2835            rust_buffer.clone(),
 2836            [ExcerptRange {
 2837                context: Point::new(0, 0)..Point::new(1, 0),
 2838                primary: None,
 2839            }],
 2840            cx,
 2841        );
 2842        multibuffer
 2843    });
 2844
 2845    cx.add_window(|cx| {
 2846        let mut editor = build_editor(multibuffer, cx);
 2847
 2848        assert_eq!(
 2849            editor.text(cx),
 2850            indoc! {"
 2851                a = 1
 2852                b = 2
 2853
 2854                const c: usize = 3;
 2855            "}
 2856        );
 2857
 2858        select_ranges(
 2859            &mut editor,
 2860            indoc! {"
 2861                «aˇ» = 1
 2862                b = 2
 2863
 2864                «const c:ˇ» usize = 3;
 2865            "},
 2866            cx,
 2867        );
 2868
 2869        editor.tab(&Tab, cx);
 2870        assert_text_with_selections(
 2871            &mut editor,
 2872            indoc! {"
 2873                  «aˇ» = 1
 2874                b = 2
 2875
 2876                    «const c:ˇ» usize = 3;
 2877            "},
 2878            cx,
 2879        );
 2880        editor.tab_prev(&TabPrev, cx);
 2881        assert_text_with_selections(
 2882            &mut editor,
 2883            indoc! {"
 2884                «aˇ» = 1
 2885                b = 2
 2886
 2887                «const c:ˇ» usize = 3;
 2888            "},
 2889            cx,
 2890        );
 2891
 2892        editor
 2893    });
 2894}
 2895
 2896#[gpui::test]
 2897async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2898    init_test(cx, |_| {});
 2899
 2900    let mut cx = EditorTestContext::new(cx).await;
 2901
 2902    // Basic backspace
 2903    cx.set_state(indoc! {"
 2904        onˇe two three
 2905        fou«rˇ» five six
 2906        seven «ˇeight nine
 2907        »ten
 2908    "});
 2909    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2910    cx.assert_editor_state(indoc! {"
 2911        oˇe two three
 2912        fouˇ five six
 2913        seven ˇten
 2914    "});
 2915
 2916    // Test backspace inside and around indents
 2917    cx.set_state(indoc! {"
 2918        zero
 2919            ˇone
 2920                ˇtwo
 2921            ˇ ˇ ˇ  three
 2922        ˇ  ˇ  four
 2923    "});
 2924    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        zero
 2927        ˇone
 2928            ˇtwo
 2929        ˇ  threeˇ  four
 2930    "});
 2931
 2932    // Test backspace with line_mode set to true
 2933    cx.update_editor(|e, _| e.selections.line_mode = true);
 2934    cx.set_state(indoc! {"
 2935        The ˇquick ˇbrown
 2936        fox jumps over
 2937        the lazy dog
 2938        ˇThe qu«ick bˇ»rown"});
 2939    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        ˇfox jumps over
 2942        the lazy dogˇ"});
 2943}
 2944
 2945#[gpui::test]
 2946async fn test_delete(cx: &mut gpui::TestAppContext) {
 2947    init_test(cx, |_| {});
 2948
 2949    let mut cx = EditorTestContext::new(cx).await;
 2950    cx.set_state(indoc! {"
 2951        onˇe two three
 2952        fou«rˇ» five six
 2953        seven «ˇeight nine
 2954        »ten
 2955    "});
 2956    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2957    cx.assert_editor_state(indoc! {"
 2958        onˇ two three
 2959        fouˇ five six
 2960        seven ˇten
 2961    "});
 2962
 2963    // Test backspace with line_mode set to true
 2964    cx.update_editor(|e, _| e.selections.line_mode = true);
 2965    cx.set_state(indoc! {"
 2966        The ˇquick ˇbrown
 2967        fox «ˇjum»ps over
 2968        the lazy dog
 2969        ˇThe qu«ick bˇ»rown"});
 2970    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2971    cx.assert_editor_state("ˇthe lazy dogˇ");
 2972}
 2973
 2974#[gpui::test]
 2975fn test_delete_line(cx: &mut TestAppContext) {
 2976    init_test(cx, |_| {});
 2977
 2978    let view = cx.add_window(|cx| {
 2979        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2980        build_editor(buffer, cx)
 2981    });
 2982    _ = view.update(cx, |view, cx| {
 2983        view.change_selections(None, cx, |s| {
 2984            s.select_display_ranges([
 2985                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2986                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2987                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2988            ])
 2989        });
 2990        view.delete_line(&DeleteLine, cx);
 2991        assert_eq!(view.display_text(cx), "ghi");
 2992        assert_eq!(
 2993            view.selections.display_ranges(cx),
 2994            vec![
 2995                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2996                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2997            ]
 2998        );
 2999    });
 3000
 3001    let view = cx.add_window(|cx| {
 3002        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3003        build_editor(buffer, cx)
 3004    });
 3005    _ = view.update(cx, |view, cx| {
 3006        view.change_selections(None, cx, |s| {
 3007            s.select_display_ranges([
 3008                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3009            ])
 3010        });
 3011        view.delete_line(&DeleteLine, cx);
 3012        assert_eq!(view.display_text(cx), "ghi\n");
 3013        assert_eq!(
 3014            view.selections.display_ranges(cx),
 3015            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3016        );
 3017    });
 3018}
 3019
 3020#[gpui::test]
 3021fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3022    init_test(cx, |_| {});
 3023
 3024    cx.add_window(|cx| {
 3025        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3026        let mut editor = build_editor(buffer.clone(), cx);
 3027        let buffer = buffer.read(cx).as_singleton().unwrap();
 3028
 3029        assert_eq!(
 3030            editor.selections.ranges::<Point>(cx),
 3031            &[Point::new(0, 0)..Point::new(0, 0)]
 3032        );
 3033
 3034        // When on single line, replace newline at end by space
 3035        editor.join_lines(&JoinLines, cx);
 3036        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3037        assert_eq!(
 3038            editor.selections.ranges::<Point>(cx),
 3039            &[Point::new(0, 3)..Point::new(0, 3)]
 3040        );
 3041
 3042        // When multiple lines are selected, remove newlines that are spanned by the selection
 3043        editor.change_selections(None, cx, |s| {
 3044            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3045        });
 3046        editor.join_lines(&JoinLines, cx);
 3047        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3048        assert_eq!(
 3049            editor.selections.ranges::<Point>(cx),
 3050            &[Point::new(0, 11)..Point::new(0, 11)]
 3051        );
 3052
 3053        // Undo should be transactional
 3054        editor.undo(&Undo, cx);
 3055        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3056        assert_eq!(
 3057            editor.selections.ranges::<Point>(cx),
 3058            &[Point::new(0, 5)..Point::new(2, 2)]
 3059        );
 3060
 3061        // When joining an empty line don't insert a space
 3062        editor.change_selections(None, cx, |s| {
 3063            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3064        });
 3065        editor.join_lines(&JoinLines, cx);
 3066        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3067        assert_eq!(
 3068            editor.selections.ranges::<Point>(cx),
 3069            [Point::new(2, 3)..Point::new(2, 3)]
 3070        );
 3071
 3072        // We can remove trailing newlines
 3073        editor.join_lines(&JoinLines, cx);
 3074        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3075        assert_eq!(
 3076            editor.selections.ranges::<Point>(cx),
 3077            [Point::new(2, 3)..Point::new(2, 3)]
 3078        );
 3079
 3080        // We don't blow up on the last line
 3081        editor.join_lines(&JoinLines, cx);
 3082        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3083        assert_eq!(
 3084            editor.selections.ranges::<Point>(cx),
 3085            [Point::new(2, 3)..Point::new(2, 3)]
 3086        );
 3087
 3088        // reset to test indentation
 3089        editor.buffer.update(cx, |buffer, cx| {
 3090            buffer.edit(
 3091                [
 3092                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3093                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3094                ],
 3095                None,
 3096                cx,
 3097            )
 3098        });
 3099
 3100        // We remove any leading spaces
 3101        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3102        editor.change_selections(None, cx, |s| {
 3103            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3104        });
 3105        editor.join_lines(&JoinLines, cx);
 3106        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3107
 3108        // We don't insert a space for a line containing only spaces
 3109        editor.join_lines(&JoinLines, cx);
 3110        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3111
 3112        // We ignore any leading tabs
 3113        editor.join_lines(&JoinLines, cx);
 3114        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3115
 3116        editor
 3117    });
 3118}
 3119
 3120#[gpui::test]
 3121fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3122    init_test(cx, |_| {});
 3123
 3124    cx.add_window(|cx| {
 3125        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3126        let mut editor = build_editor(buffer.clone(), cx);
 3127        let buffer = buffer.read(cx).as_singleton().unwrap();
 3128
 3129        editor.change_selections(None, cx, |s| {
 3130            s.select_ranges([
 3131                Point::new(0, 2)..Point::new(1, 1),
 3132                Point::new(1, 2)..Point::new(1, 2),
 3133                Point::new(3, 1)..Point::new(3, 2),
 3134            ])
 3135        });
 3136
 3137        editor.join_lines(&JoinLines, cx);
 3138        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3139
 3140        assert_eq!(
 3141            editor.selections.ranges::<Point>(cx),
 3142            [
 3143                Point::new(0, 7)..Point::new(0, 7),
 3144                Point::new(1, 3)..Point::new(1, 3)
 3145            ]
 3146        );
 3147        editor
 3148    });
 3149}
 3150
 3151#[gpui::test]
 3152async fn test_join_lines_with_git_diff_base(
 3153    executor: BackgroundExecutor,
 3154    cx: &mut gpui::TestAppContext,
 3155) {
 3156    init_test(cx, |_| {});
 3157
 3158    let mut cx = EditorTestContext::new(cx).await;
 3159
 3160    let diff_base = r#"
 3161        Line 0
 3162        Line 1
 3163        Line 2
 3164        Line 3
 3165        "#
 3166    .unindent();
 3167
 3168    cx.set_state(
 3169        &r#"
 3170        ˇLine 0
 3171        Line 1
 3172        Line 2
 3173        Line 3
 3174        "#
 3175        .unindent(),
 3176    );
 3177
 3178    cx.set_diff_base(Some(&diff_base));
 3179    executor.run_until_parked();
 3180
 3181    // Join lines
 3182    cx.update_editor(|editor, cx| {
 3183        editor.join_lines(&JoinLines, cx);
 3184    });
 3185    executor.run_until_parked();
 3186
 3187    cx.assert_editor_state(
 3188        &r#"
 3189        Line 0ˇ Line 1
 3190        Line 2
 3191        Line 3
 3192        "#
 3193        .unindent(),
 3194    );
 3195    // Join again
 3196    cx.update_editor(|editor, cx| {
 3197        editor.join_lines(&JoinLines, cx);
 3198    });
 3199    executor.run_until_parked();
 3200
 3201    cx.assert_editor_state(
 3202        &r#"
 3203        Line 0 Line 1ˇ Line 2
 3204        Line 3
 3205        "#
 3206        .unindent(),
 3207    );
 3208}
 3209
 3210#[gpui::test]
 3211async fn test_custom_newlines_cause_no_false_positive_diffs(
 3212    executor: BackgroundExecutor,
 3213    cx: &mut gpui::TestAppContext,
 3214) {
 3215    init_test(cx, |_| {});
 3216    let mut cx = EditorTestContext::new(cx).await;
 3217    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3218    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3219    executor.run_until_parked();
 3220
 3221    cx.update_editor(|editor, cx| {
 3222        assert_eq!(
 3223            editor
 3224                .buffer()
 3225                .read(cx)
 3226                .snapshot(cx)
 3227                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3228                .collect::<Vec<_>>(),
 3229            Vec::new(),
 3230            "Should not have any diffs for files with custom newlines"
 3231        );
 3232    });
 3233}
 3234
 3235#[gpui::test]
 3236async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3237    init_test(cx, |_| {});
 3238
 3239    let mut cx = EditorTestContext::new(cx).await;
 3240
 3241    // Test sort_lines_case_insensitive()
 3242    cx.set_state(indoc! {"
 3243        «z
 3244        y
 3245        x
 3246        Z
 3247        Y
 3248        Xˇ»
 3249    "});
 3250    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        «x
 3253        X
 3254        y
 3255        Y
 3256        z
 3257        Zˇ»
 3258    "});
 3259
 3260    // Test reverse_lines()
 3261    cx.set_state(indoc! {"
 3262        «5
 3263        4
 3264        3
 3265        2
 3266        1ˇ»
 3267    "});
 3268    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3269    cx.assert_editor_state(indoc! {"
 3270        «1
 3271        2
 3272        3
 3273        4
 3274        5ˇ»
 3275    "});
 3276
 3277    // Skip testing shuffle_line()
 3278
 3279    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3280    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3281
 3282    // Don't manipulate when cursor is on single line, but expand the selection
 3283    cx.set_state(indoc! {"
 3284        ddˇdd
 3285        ccc
 3286        bb
 3287        a
 3288    "});
 3289    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3290    cx.assert_editor_state(indoc! {"
 3291        «ddddˇ»
 3292        ccc
 3293        bb
 3294        a
 3295    "});
 3296
 3297    // Basic manipulate case
 3298    // Start selection moves to column 0
 3299    // End of selection shrinks to fit shorter line
 3300    cx.set_state(indoc! {"
 3301        dd«d
 3302        ccc
 3303        bb
 3304        aaaaaˇ»
 3305    "});
 3306    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3307    cx.assert_editor_state(indoc! {"
 3308        «aaaaa
 3309        bb
 3310        ccc
 3311        dddˇ»
 3312    "});
 3313
 3314    // Manipulate case with newlines
 3315    cx.set_state(indoc! {"
 3316        dd«d
 3317        ccc
 3318
 3319        bb
 3320        aaaaa
 3321
 3322        ˇ»
 3323    "});
 3324    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3325    cx.assert_editor_state(indoc! {"
 3326        «
 3327
 3328        aaaaa
 3329        bb
 3330        ccc
 3331        dddˇ»
 3332
 3333    "});
 3334
 3335    // Adding new line
 3336    cx.set_state(indoc! {"
 3337        aa«a
 3338        bbˇ»b
 3339    "});
 3340    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3341    cx.assert_editor_state(indoc! {"
 3342        «aaa
 3343        bbb
 3344        added_lineˇ»
 3345    "});
 3346
 3347    // Removing line
 3348    cx.set_state(indoc! {"
 3349        aa«a
 3350        bbbˇ»
 3351    "});
 3352    cx.update_editor(|e, cx| {
 3353        e.manipulate_lines(cx, |lines| {
 3354            lines.pop();
 3355        })
 3356    });
 3357    cx.assert_editor_state(indoc! {"
 3358        «aaaˇ»
 3359    "});
 3360
 3361    // Removing all lines
 3362    cx.set_state(indoc! {"
 3363        aa«a
 3364        bbbˇ»
 3365    "});
 3366    cx.update_editor(|e, cx| {
 3367        e.manipulate_lines(cx, |lines| {
 3368            lines.drain(..);
 3369        })
 3370    });
 3371    cx.assert_editor_state(indoc! {"
 3372        ˇ
 3373    "});
 3374}
 3375
 3376#[gpui::test]
 3377async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3378    init_test(cx, |_| {});
 3379
 3380    let mut cx = EditorTestContext::new(cx).await;
 3381
 3382    // Consider continuous selection as single selection
 3383    cx.set_state(indoc! {"
 3384        Aaa«aa
 3385        cˇ»c«c
 3386        bb
 3387        aaaˇ»aa
 3388    "});
 3389    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3390    cx.assert_editor_state(indoc! {"
 3391        «Aaaaa
 3392        ccc
 3393        bb
 3394        aaaaaˇ»
 3395    "});
 3396
 3397    cx.set_state(indoc! {"
 3398        Aaa«aa
 3399        cˇ»c«c
 3400        bb
 3401        aaaˇ»aa
 3402    "});
 3403    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3404    cx.assert_editor_state(indoc! {"
 3405        «Aaaaa
 3406        ccc
 3407        bbˇ»
 3408    "});
 3409
 3410    // Consider non continuous selection as distinct dedup operations
 3411    cx.set_state(indoc! {"
 3412        «aaaaa
 3413        bb
 3414        aaaaa
 3415        aaaaaˇ»
 3416
 3417        aaa«aaˇ»
 3418    "});
 3419    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3420    cx.assert_editor_state(indoc! {"
 3421        «aaaaa
 3422        bbˇ»
 3423
 3424        «aaaaaˇ»
 3425    "});
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3430    init_test(cx, |_| {});
 3431
 3432    let mut cx = EditorTestContext::new(cx).await;
 3433
 3434    cx.set_state(indoc! {"
 3435        «Aaa
 3436        aAa
 3437        Aaaˇ»
 3438    "});
 3439    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        «Aaa
 3442        aAaˇ»
 3443    "});
 3444
 3445    cx.set_state(indoc! {"
 3446        «Aaa
 3447        aAa
 3448        aaAˇ»
 3449    "});
 3450    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3451    cx.assert_editor_state(indoc! {"
 3452        «Aaaˇ»
 3453    "});
 3454}
 3455
 3456#[gpui::test]
 3457async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    let mut cx = EditorTestContext::new(cx).await;
 3461
 3462    // Manipulate with multiple selections on a single line
 3463    cx.set_state(indoc! {"
 3464        dd«dd
 3465        cˇ»c«c
 3466        bb
 3467        aaaˇ»aa
 3468    "});
 3469    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3470    cx.assert_editor_state(indoc! {"
 3471        «aaaaa
 3472        bb
 3473        ccc
 3474        ddddˇ»
 3475    "});
 3476
 3477    // Manipulate with multiple disjoin selections
 3478    cx.set_state(indoc! {"
 3479 3480        4
 3481        3
 3482        2
 3483        1ˇ»
 3484
 3485        dd«dd
 3486        ccc
 3487        bb
 3488        aaaˇ»aa
 3489    "});
 3490    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3491    cx.assert_editor_state(indoc! {"
 3492        «1
 3493        2
 3494        3
 3495        4
 3496        5ˇ»
 3497
 3498        «aaaaa
 3499        bb
 3500        ccc
 3501        ddddˇ»
 3502    "});
 3503
 3504    // Adding lines on each selection
 3505    cx.set_state(indoc! {"
 3506 3507        1ˇ»
 3508
 3509        bb«bb
 3510        aaaˇ»aa
 3511    "});
 3512    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3513    cx.assert_editor_state(indoc! {"
 3514        «2
 3515        1
 3516        added lineˇ»
 3517
 3518        «bbbb
 3519        aaaaa
 3520        added lineˇ»
 3521    "});
 3522
 3523    // Removing lines on each selection
 3524    cx.set_state(indoc! {"
 3525 3526        1ˇ»
 3527
 3528        bb«bb
 3529        aaaˇ»aa
 3530    "});
 3531    cx.update_editor(|e, cx| {
 3532        e.manipulate_lines(cx, |lines| {
 3533            lines.pop();
 3534        })
 3535    });
 3536    cx.assert_editor_state(indoc! {"
 3537        «2ˇ»
 3538
 3539        «bbbbˇ»
 3540    "});
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_manipulate_text(cx: &mut TestAppContext) {
 3545    init_test(cx, |_| {});
 3546
 3547    let mut cx = EditorTestContext::new(cx).await;
 3548
 3549    // Test convert_to_upper_case()
 3550    cx.set_state(indoc! {"
 3551        «hello worldˇ»
 3552    "});
 3553    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3554    cx.assert_editor_state(indoc! {"
 3555        «HELLO WORLDˇ»
 3556    "});
 3557
 3558    // Test convert_to_lower_case()
 3559    cx.set_state(indoc! {"
 3560        «HELLO WORLDˇ»
 3561    "});
 3562    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3563    cx.assert_editor_state(indoc! {"
 3564        «hello worldˇ»
 3565    "});
 3566
 3567    // Test multiple line, single selection case
 3568    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3569    cx.set_state(indoc! {"
 3570        «The quick brown
 3571        fox jumps over
 3572        the lazy dogˇ»
 3573    "});
 3574    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «The Quick Brown
 3577        Fox Jumps Over
 3578        The Lazy Dogˇ»
 3579    "});
 3580
 3581    // Test multiple line, single selection case
 3582    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3583    cx.set_state(indoc! {"
 3584        «The quick brown
 3585        fox jumps over
 3586        the lazy dogˇ»
 3587    "});
 3588    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3589    cx.assert_editor_state(indoc! {"
 3590        «TheQuickBrown
 3591        FoxJumpsOver
 3592        TheLazyDogˇ»
 3593    "});
 3594
 3595    // From here on out, test more complex cases of manipulate_text()
 3596
 3597    // Test no selection case - should affect words cursors are in
 3598    // Cursor at beginning, middle, and end of word
 3599    cx.set_state(indoc! {"
 3600        ˇhello big beauˇtiful worldˇ
 3601    "});
 3602    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3605    "});
 3606
 3607    // Test multiple selections on a single line and across multiple lines
 3608    cx.set_state(indoc! {"
 3609        «Theˇ» quick «brown
 3610        foxˇ» jumps «overˇ»
 3611        the «lazyˇ» dog
 3612    "});
 3613    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3614    cx.assert_editor_state(indoc! {"
 3615        «THEˇ» quick «BROWN
 3616        FOXˇ» jumps «OVERˇ»
 3617        the «LAZYˇ» dog
 3618    "});
 3619
 3620    // Test case where text length grows
 3621    cx.set_state(indoc! {"
 3622        «tschüߡ»
 3623    "});
 3624    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3625    cx.assert_editor_state(indoc! {"
 3626        «TSCHÜSSˇ»
 3627    "});
 3628
 3629    // Test to make sure we don't crash when text shrinks
 3630    cx.set_state(indoc! {"
 3631        aaa_bbbˇ
 3632    "});
 3633    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3634    cx.assert_editor_state(indoc! {"
 3635        «aaaBbbˇ»
 3636    "});
 3637
 3638    // Test to make sure we all aware of the fact that each word can grow and shrink
 3639    // Final selections should be aware of this fact
 3640    cx.set_state(indoc! {"
 3641        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3642    "});
 3643    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3644    cx.assert_editor_state(indoc! {"
 3645        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3646    "});
 3647
 3648    cx.set_state(indoc! {"
 3649        «hElLo, WoRld!ˇ»
 3650    "});
 3651    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3652    cx.assert_editor_state(indoc! {"
 3653        «HeLlO, wOrLD!ˇ»
 3654    "});
 3655}
 3656
 3657#[gpui::test]
 3658fn test_duplicate_line(cx: &mut TestAppContext) {
 3659    init_test(cx, |_| {});
 3660
 3661    let view = cx.add_window(|cx| {
 3662        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3663        build_editor(buffer, cx)
 3664    });
 3665    _ = view.update(cx, |view, cx| {
 3666        view.change_selections(None, cx, |s| {
 3667            s.select_display_ranges([
 3668                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3669                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3670                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3671                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3672            ])
 3673        });
 3674        view.duplicate_line_down(&DuplicateLineDown, cx);
 3675        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3676        assert_eq!(
 3677            view.selections.display_ranges(cx),
 3678            vec![
 3679                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3680                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3681                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3682                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3683            ]
 3684        );
 3685    });
 3686
 3687    let view = cx.add_window(|cx| {
 3688        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3689        build_editor(buffer, cx)
 3690    });
 3691    _ = view.update(cx, |view, cx| {
 3692        view.change_selections(None, cx, |s| {
 3693            s.select_display_ranges([
 3694                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3695                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3696            ])
 3697        });
 3698        view.duplicate_line_down(&DuplicateLineDown, cx);
 3699        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3700        assert_eq!(
 3701            view.selections.display_ranges(cx),
 3702            vec![
 3703                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3704                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3705            ]
 3706        );
 3707    });
 3708
 3709    // With `move_upwards` the selections stay in place, except for
 3710    // the lines inserted above them
 3711    let view = cx.add_window(|cx| {
 3712        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3713        build_editor(buffer, cx)
 3714    });
 3715    _ = view.update(cx, |view, cx| {
 3716        view.change_selections(None, cx, |s| {
 3717            s.select_display_ranges([
 3718                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3719                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3720                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3721                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3722            ])
 3723        });
 3724        view.duplicate_line_up(&DuplicateLineUp, cx);
 3725        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3726        assert_eq!(
 3727            view.selections.display_ranges(cx),
 3728            vec![
 3729                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3730                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3731                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3732                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3733            ]
 3734        );
 3735    });
 3736
 3737    let view = cx.add_window(|cx| {
 3738        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3739        build_editor(buffer, cx)
 3740    });
 3741    _ = view.update(cx, |view, cx| {
 3742        view.change_selections(None, cx, |s| {
 3743            s.select_display_ranges([
 3744                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3745                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3746            ])
 3747        });
 3748        view.duplicate_line_up(&DuplicateLineUp, cx);
 3749        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3750        assert_eq!(
 3751            view.selections.display_ranges(cx),
 3752            vec![
 3753                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3754                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3755            ]
 3756        );
 3757    });
 3758}
 3759
 3760#[gpui::test]
 3761fn test_move_line_up_down(cx: &mut TestAppContext) {
 3762    init_test(cx, |_| {});
 3763
 3764    let view = cx.add_window(|cx| {
 3765        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3766        build_editor(buffer, cx)
 3767    });
 3768    _ = view.update(cx, |view, cx| {
 3769        view.fold_ranges(
 3770            vec![
 3771                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3772                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3773                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3774            ],
 3775            true,
 3776            cx,
 3777        );
 3778        view.change_selections(None, cx, |s| {
 3779            s.select_display_ranges([
 3780                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3781                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3782                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3783                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3784            ])
 3785        });
 3786        assert_eq!(
 3787            view.display_text(cx),
 3788            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3789        );
 3790
 3791        view.move_line_up(&MoveLineUp, cx);
 3792        assert_eq!(
 3793            view.display_text(cx),
 3794            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3795        );
 3796        assert_eq!(
 3797            view.selections.display_ranges(cx),
 3798            vec![
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3800                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3801                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3802                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3803            ]
 3804        );
 3805    });
 3806
 3807    _ = view.update(cx, |view, cx| {
 3808        view.move_line_down(&MoveLineDown, cx);
 3809        assert_eq!(
 3810            view.display_text(cx),
 3811            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3812        );
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3818                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3819                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3820            ]
 3821        );
 3822    });
 3823
 3824    _ = view.update(cx, |view, cx| {
 3825        view.move_line_down(&MoveLineDown, cx);
 3826        assert_eq!(
 3827            view.display_text(cx),
 3828            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3829        );
 3830        assert_eq!(
 3831            view.selections.display_ranges(cx),
 3832            vec![
 3833                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3834                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3835                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3836                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3837            ]
 3838        );
 3839    });
 3840
 3841    _ = view.update(cx, |view, cx| {
 3842        view.move_line_up(&MoveLineUp, cx);
 3843        assert_eq!(
 3844            view.display_text(cx),
 3845            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3846        );
 3847        assert_eq!(
 3848            view.selections.display_ranges(cx),
 3849            vec![
 3850                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3851                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3852                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3853                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3854            ]
 3855        );
 3856    });
 3857}
 3858
 3859#[gpui::test]
 3860fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3861    init_test(cx, |_| {});
 3862
 3863    let editor = cx.add_window(|cx| {
 3864        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3865        build_editor(buffer, cx)
 3866    });
 3867    _ = editor.update(cx, |editor, cx| {
 3868        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3869        editor.insert_blocks(
 3870            [BlockProperties {
 3871                style: BlockStyle::Fixed,
 3872                position: snapshot.anchor_after(Point::new(2, 0)),
 3873                disposition: BlockDisposition::Below,
 3874                height: 1,
 3875                render: Box::new(|_| div().into_any()),
 3876                priority: 0,
 3877            }],
 3878            Some(Autoscroll::fit()),
 3879            cx,
 3880        );
 3881        editor.change_selections(None, cx, |s| {
 3882            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3883        });
 3884        editor.move_line_down(&MoveLineDown, cx);
 3885    });
 3886}
 3887
 3888#[gpui::test]
 3889fn test_transpose(cx: &mut TestAppContext) {
 3890    init_test(cx, |_| {});
 3891
 3892    _ = cx.add_window(|cx| {
 3893        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3894        editor.set_style(EditorStyle::default(), cx);
 3895        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3896        editor.transpose(&Default::default(), cx);
 3897        assert_eq!(editor.text(cx), "bac");
 3898        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3899
 3900        editor.transpose(&Default::default(), cx);
 3901        assert_eq!(editor.text(cx), "bca");
 3902        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3903
 3904        editor.transpose(&Default::default(), cx);
 3905        assert_eq!(editor.text(cx), "bac");
 3906        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3907
 3908        editor
 3909    });
 3910
 3911    _ = cx.add_window(|cx| {
 3912        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3913        editor.set_style(EditorStyle::default(), cx);
 3914        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3915        editor.transpose(&Default::default(), cx);
 3916        assert_eq!(editor.text(cx), "acb\nde");
 3917        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3918
 3919        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3920        editor.transpose(&Default::default(), cx);
 3921        assert_eq!(editor.text(cx), "acbd\ne");
 3922        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3923
 3924        editor.transpose(&Default::default(), cx);
 3925        assert_eq!(editor.text(cx), "acbde\n");
 3926        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3927
 3928        editor.transpose(&Default::default(), cx);
 3929        assert_eq!(editor.text(cx), "acbd\ne");
 3930        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3931
 3932        editor
 3933    });
 3934
 3935    _ = cx.add_window(|cx| {
 3936        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3937        editor.set_style(EditorStyle::default(), cx);
 3938        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3939        editor.transpose(&Default::default(), cx);
 3940        assert_eq!(editor.text(cx), "bacd\ne");
 3941        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3942
 3943        editor.transpose(&Default::default(), cx);
 3944        assert_eq!(editor.text(cx), "bcade\n");
 3945        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3946
 3947        editor.transpose(&Default::default(), cx);
 3948        assert_eq!(editor.text(cx), "bcda\ne");
 3949        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3950
 3951        editor.transpose(&Default::default(), cx);
 3952        assert_eq!(editor.text(cx), "bcade\n");
 3953        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3954
 3955        editor.transpose(&Default::default(), cx);
 3956        assert_eq!(editor.text(cx), "bcaed\n");
 3957        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3958
 3959        editor
 3960    });
 3961
 3962    _ = cx.add_window(|cx| {
 3963        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3964        editor.set_style(EditorStyle::default(), cx);
 3965        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3966        editor.transpose(&Default::default(), cx);
 3967        assert_eq!(editor.text(cx), "🏀🍐✋");
 3968        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3969
 3970        editor.transpose(&Default::default(), cx);
 3971        assert_eq!(editor.text(cx), "🏀✋🍐");
 3972        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3973
 3974        editor.transpose(&Default::default(), cx);
 3975        assert_eq!(editor.text(cx), "🏀🍐✋");
 3976        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3977
 3978        editor
 3979    });
 3980}
 3981
 3982#[gpui::test]
 3983async fn test_rewrap(cx: &mut TestAppContext) {
 3984    init_test(cx, |_| {});
 3985
 3986    let mut cx = EditorTestContext::new(cx).await;
 3987
 3988    {
 3989        let language = Arc::new(Language::new(
 3990            LanguageConfig {
 3991                line_comments: vec!["// ".into()],
 3992                ..LanguageConfig::default()
 3993            },
 3994            None,
 3995        ));
 3996        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3997
 3998        let unwrapped_text = indoc! {"
 3999            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4000        "};
 4001
 4002        let wrapped_text = indoc! {"
 4003            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4004            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4005            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4006            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4007            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4008            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4009            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4010            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4011            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4012            // porttitor id. Aliquam id accumsan eros.ˇ
 4013        "};
 4014
 4015        cx.set_state(unwrapped_text);
 4016        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4017        cx.assert_editor_state(wrapped_text);
 4018    }
 4019
 4020    // Test that rewrapping works inside of a selection
 4021    {
 4022        let language = Arc::new(Language::new(
 4023            LanguageConfig {
 4024                line_comments: vec!["// ".into()],
 4025                ..LanguageConfig::default()
 4026            },
 4027            None,
 4028        ));
 4029        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4030
 4031        let unwrapped_text = indoc! {"
 4032            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4033        "};
 4034
 4035        let wrapped_text = indoc! {"
 4036            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4037            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4038            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4039            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4040            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4041            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4042            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4043            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4044            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4045            // porttitor id. Aliquam id accumsan eros.ˇ
 4046        "};
 4047
 4048        cx.set_state(unwrapped_text);
 4049        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4050        cx.assert_editor_state(wrapped_text);
 4051    }
 4052
 4053    // Test that cursors that expand to the same region are collapsed.
 4054    {
 4055        let language = Arc::new(Language::new(
 4056            LanguageConfig {
 4057                line_comments: vec!["// ".into()],
 4058                ..LanguageConfig::default()
 4059            },
 4060            None,
 4061        ));
 4062        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4063
 4064        let unwrapped_text = indoc! {"
 4065            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4066            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4067            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4068            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4069        "};
 4070
 4071        let wrapped_text = indoc! {"
 4072            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4073            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4074            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4075            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4076            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4077            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4078            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4079            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4080            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4081            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4082        "};
 4083
 4084        cx.set_state(unwrapped_text);
 4085        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4086        cx.assert_editor_state(wrapped_text);
 4087    }
 4088
 4089    // Test that non-contiguous selections are treated separately.
 4090    {
 4091        let language = Arc::new(Language::new(
 4092            LanguageConfig {
 4093                line_comments: vec!["// ".into()],
 4094                ..LanguageConfig::default()
 4095            },
 4096            None,
 4097        ));
 4098        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4099
 4100        let unwrapped_text = indoc! {"
 4101            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4102            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4103            //
 4104            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4105            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4106        "};
 4107
 4108        let wrapped_text = indoc! {"
 4109            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4110            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4111            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4112            //
 4113            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4114            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4115            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4116            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4117            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4118            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4119            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4120        "};
 4121
 4122        cx.set_state(unwrapped_text);
 4123        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4124        cx.assert_editor_state(wrapped_text);
 4125    }
 4126
 4127    // Test that different comment prefixes are supported.
 4128    {
 4129        let language = Arc::new(Language::new(
 4130            LanguageConfig {
 4131                line_comments: vec!["# ".into()],
 4132                ..LanguageConfig::default()
 4133            },
 4134            None,
 4135        ));
 4136        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4137
 4138        let unwrapped_text = indoc! {"
 4139            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4140        "};
 4141
 4142        let wrapped_text = indoc! {"
 4143            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4144            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4145            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4146            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4147            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4148            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4149            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4150            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4151            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4152            # accumsan eros.ˇ
 4153        "};
 4154
 4155        cx.set_state(unwrapped_text);
 4156        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4157        cx.assert_editor_state(wrapped_text);
 4158    }
 4159
 4160    // Test that rewrapping is ignored outside of comments in most languages.
 4161    {
 4162        let language = Arc::new(Language::new(
 4163            LanguageConfig {
 4164                line_comments: vec!["// ".into(), "/// ".into()],
 4165                ..LanguageConfig::default()
 4166            },
 4167            Some(tree_sitter_rust::LANGUAGE.into()),
 4168        ));
 4169        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4170
 4171        let unwrapped_text = indoc! {"
 4172            /// Adds two numbers.
 4173            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4174            fn add(a: u32, b: u32) -> u32 {
 4175                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4176            }
 4177        "};
 4178
 4179        let wrapped_text = indoc! {"
 4180            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4181            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4182            fn add(a: u32, b: u32) -> u32 {
 4183                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4184            }
 4185        "};
 4186
 4187        cx.set_state(unwrapped_text);
 4188        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4189        cx.assert_editor_state(wrapped_text);
 4190    }
 4191
 4192    // Test that rewrapping works in Markdown and Plain Text languages.
 4193    {
 4194        let markdown_language = Arc::new(Language::new(
 4195            LanguageConfig {
 4196                name: "Markdown".into(),
 4197                ..LanguageConfig::default()
 4198            },
 4199            None,
 4200        ));
 4201        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4202
 4203        let unwrapped_text = indoc! {"
 4204            # Hello
 4205
 4206            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4207        "};
 4208
 4209        let wrapped_text = indoc! {"
 4210            # Hello
 4211
 4212            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4213            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4214            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4215            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4216            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4217            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4218            Integer sit amet scelerisque nisi.ˇ
 4219        "};
 4220
 4221        cx.set_state(unwrapped_text);
 4222        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4223        cx.assert_editor_state(wrapped_text);
 4224
 4225        let plaintext_language = Arc::new(Language::new(
 4226            LanguageConfig {
 4227                name: "Plain Text".into(),
 4228                ..LanguageConfig::default()
 4229            },
 4230            None,
 4231        ));
 4232        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4233
 4234        let unwrapped_text = indoc! {"
 4235            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4236        "};
 4237
 4238        let wrapped_text = indoc! {"
 4239            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4240            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4241            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4242            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4243            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4244            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4245            Integer sit amet scelerisque nisi.ˇ
 4246        "};
 4247
 4248        cx.set_state(unwrapped_text);
 4249        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4250        cx.assert_editor_state(wrapped_text);
 4251    }
 4252
 4253    // Test rewrapping unaligned comments in a selection.
 4254    {
 4255        let language = Arc::new(Language::new(
 4256            LanguageConfig {
 4257                line_comments: vec!["// ".into(), "/// ".into()],
 4258                ..LanguageConfig::default()
 4259            },
 4260            Some(tree_sitter_rust::LANGUAGE.into()),
 4261        ));
 4262        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4263
 4264        let unwrapped_text = indoc! {"
 4265            fn foo() {
 4266                if true {
 4267            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4268            // Praesent semper egestas tellus id dignissim.ˇ»
 4269                    do_something();
 4270                } else {
 4271                    //
 4272                }
 4273            }
 4274        "};
 4275
 4276        let wrapped_text = indoc! {"
 4277            fn foo() {
 4278                if true {
 4279                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4280                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4281                    // egestas tellus id dignissim.ˇ
 4282                    do_something();
 4283                } else {
 4284                    //
 4285                }
 4286            }
 4287        "};
 4288
 4289        cx.set_state(unwrapped_text);
 4290        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4291        cx.assert_editor_state(wrapped_text);
 4292
 4293        let unwrapped_text = indoc! {"
 4294            fn foo() {
 4295                if true {
 4296            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4297            // Praesent semper egestas tellus id dignissim.»
 4298                    do_something();
 4299                } else {
 4300                    //
 4301                }
 4302
 4303            }
 4304        "};
 4305
 4306        let wrapped_text = indoc! {"
 4307            fn foo() {
 4308                if true {
 4309                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4310                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4311                    // egestas tellus id dignissim.ˇ
 4312                    do_something();
 4313                } else {
 4314                    //
 4315                }
 4316
 4317            }
 4318        "};
 4319
 4320        cx.set_state(unwrapped_text);
 4321        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4322        cx.assert_editor_state(wrapped_text);
 4323    }
 4324}
 4325
 4326#[gpui::test]
 4327async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4328    init_test(cx, |_| {});
 4329
 4330    let mut cx = EditorTestContext::new(cx).await;
 4331
 4332    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4333    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4334    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4335
 4336    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4337    cx.set_state("two ˇfour ˇsix ˇ");
 4338    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4339    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4340
 4341    // Paste again but with only two cursors. Since the number of cursors doesn't
 4342    // match the number of slices in the clipboard, the entire clipboard text
 4343    // is pasted at each cursor.
 4344    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4345    cx.update_editor(|e, cx| {
 4346        e.handle_input("( ", cx);
 4347        e.paste(&Paste, cx);
 4348        e.handle_input(") ", cx);
 4349    });
 4350    cx.assert_editor_state(
 4351        &([
 4352            "( one✅ ",
 4353            "three ",
 4354            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4355            "three ",
 4356            "five ) ˇ",
 4357        ]
 4358        .join("\n")),
 4359    );
 4360
 4361    // Cut with three selections, one of which is full-line.
 4362    cx.set_state(indoc! {"
 4363        1«2ˇ»3
 4364        4ˇ567
 4365        «8ˇ»9"});
 4366    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4367    cx.assert_editor_state(indoc! {"
 4368        1ˇ3
 4369        ˇ9"});
 4370
 4371    // Paste with three selections, noticing how the copied selection that was full-line
 4372    // gets inserted before the second cursor.
 4373    cx.set_state(indoc! {"
 4374        1ˇ3
 4375 4376        «oˇ»ne"});
 4377    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4378    cx.assert_editor_state(indoc! {"
 4379        12ˇ3
 4380        4567
 4381 4382        8ˇne"});
 4383
 4384    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4385    cx.set_state(indoc! {"
 4386        The quick brown
 4387        fox juˇmps over
 4388        the lazy dog"});
 4389    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4390    assert_eq!(
 4391        cx.read_from_clipboard()
 4392            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4393        Some("fox jumps over\n".to_string())
 4394    );
 4395
 4396    // Paste with three selections, noticing how the copied full-line selection is inserted
 4397    // before the empty selections but replaces the selection that is non-empty.
 4398    cx.set_state(indoc! {"
 4399        Tˇhe quick brown
 4400        «foˇ»x jumps over
 4401        tˇhe lazy dog"});
 4402    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4403    cx.assert_editor_state(indoc! {"
 4404        fox jumps over
 4405        Tˇhe quick brown
 4406        fox jumps over
 4407        ˇx jumps over
 4408        fox jumps over
 4409        tˇhe lazy dog"});
 4410}
 4411
 4412#[gpui::test]
 4413async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4414    init_test(cx, |_| {});
 4415
 4416    let mut cx = EditorTestContext::new(cx).await;
 4417    let language = Arc::new(Language::new(
 4418        LanguageConfig::default(),
 4419        Some(tree_sitter_rust::LANGUAGE.into()),
 4420    ));
 4421    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4422
 4423    // Cut an indented block, without the leading whitespace.
 4424    cx.set_state(indoc! {"
 4425        const a: B = (
 4426            c(),
 4427            «d(
 4428                e,
 4429                f
 4430            )ˇ»
 4431        );
 4432    "});
 4433    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4434    cx.assert_editor_state(indoc! {"
 4435        const a: B = (
 4436            c(),
 4437            ˇ
 4438        );
 4439    "});
 4440
 4441    // Paste it at the same position.
 4442    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4443    cx.assert_editor_state(indoc! {"
 4444        const a: B = (
 4445            c(),
 4446            d(
 4447                e,
 4448                f
 4449 4450        );
 4451    "});
 4452
 4453    // Paste it at a line with a lower indent level.
 4454    cx.set_state(indoc! {"
 4455        ˇ
 4456        const a: B = (
 4457            c(),
 4458        );
 4459    "});
 4460    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4461    cx.assert_editor_state(indoc! {"
 4462        d(
 4463            e,
 4464            f
 4465 4466        const a: B = (
 4467            c(),
 4468        );
 4469    "});
 4470
 4471    // Cut an indented block, with the leading whitespace.
 4472    cx.set_state(indoc! {"
 4473        const a: B = (
 4474            c(),
 4475        «    d(
 4476                e,
 4477                f
 4478            )
 4479        ˇ»);
 4480    "});
 4481    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4482    cx.assert_editor_state(indoc! {"
 4483        const a: B = (
 4484            c(),
 4485        ˇ);
 4486    "});
 4487
 4488    // Paste it at the same position.
 4489    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4490    cx.assert_editor_state(indoc! {"
 4491        const a: B = (
 4492            c(),
 4493            d(
 4494                e,
 4495                f
 4496            )
 4497        ˇ);
 4498    "});
 4499
 4500    // Paste it at a line with a higher indent level.
 4501    cx.set_state(indoc! {"
 4502        const a: B = (
 4503            c(),
 4504            d(
 4505                e,
 4506 4507            )
 4508        );
 4509    "});
 4510    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4511    cx.assert_editor_state(indoc! {"
 4512        const a: B = (
 4513            c(),
 4514            d(
 4515                e,
 4516                f    d(
 4517                    e,
 4518                    f
 4519                )
 4520        ˇ
 4521            )
 4522        );
 4523    "});
 4524}
 4525
 4526#[gpui::test]
 4527fn test_select_all(cx: &mut TestAppContext) {
 4528    init_test(cx, |_| {});
 4529
 4530    let view = cx.add_window(|cx| {
 4531        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4532        build_editor(buffer, cx)
 4533    });
 4534    _ = view.update(cx, |view, cx| {
 4535        view.select_all(&SelectAll, cx);
 4536        assert_eq!(
 4537            view.selections.display_ranges(cx),
 4538            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4539        );
 4540    });
 4541}
 4542
 4543#[gpui::test]
 4544fn test_select_line(cx: &mut TestAppContext) {
 4545    init_test(cx, |_| {});
 4546
 4547    let view = cx.add_window(|cx| {
 4548        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4549        build_editor(buffer, cx)
 4550    });
 4551    _ = view.update(cx, |view, cx| {
 4552        view.change_selections(None, cx, |s| {
 4553            s.select_display_ranges([
 4554                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4555                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4556                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4557                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4558            ])
 4559        });
 4560        view.select_line(&SelectLine, cx);
 4561        assert_eq!(
 4562            view.selections.display_ranges(cx),
 4563            vec![
 4564                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4565                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4566            ]
 4567        );
 4568    });
 4569
 4570    _ = view.update(cx, |view, cx| {
 4571        view.select_line(&SelectLine, cx);
 4572        assert_eq!(
 4573            view.selections.display_ranges(cx),
 4574            vec![
 4575                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4576                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4577            ]
 4578        );
 4579    });
 4580
 4581    _ = view.update(cx, |view, cx| {
 4582        view.select_line(&SelectLine, cx);
 4583        assert_eq!(
 4584            view.selections.display_ranges(cx),
 4585            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4586        );
 4587    });
 4588}
 4589
 4590#[gpui::test]
 4591fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4592    init_test(cx, |_| {});
 4593
 4594    let view = cx.add_window(|cx| {
 4595        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4596        build_editor(buffer, cx)
 4597    });
 4598    _ = view.update(cx, |view, cx| {
 4599        view.fold_ranges(
 4600            vec![
 4601                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4602                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4603                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4604            ],
 4605            true,
 4606            cx,
 4607        );
 4608        view.change_selections(None, cx, |s| {
 4609            s.select_display_ranges([
 4610                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4611                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4612                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4613                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4614            ])
 4615        });
 4616        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4617    });
 4618
 4619    _ = view.update(cx, |view, cx| {
 4620        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4621        assert_eq!(
 4622            view.display_text(cx),
 4623            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4624        );
 4625        assert_eq!(
 4626            view.selections.display_ranges(cx),
 4627            [
 4628                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4629                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4630                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4631                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4632            ]
 4633        );
 4634    });
 4635
 4636    _ = view.update(cx, |view, cx| {
 4637        view.change_selections(None, cx, |s| {
 4638            s.select_display_ranges([
 4639                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4640            ])
 4641        });
 4642        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4643        assert_eq!(
 4644            view.display_text(cx),
 4645            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4646        );
 4647        assert_eq!(
 4648            view.selections.display_ranges(cx),
 4649            [
 4650                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4651                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4652                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4653                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4654                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4655                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4656                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4657                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4658            ]
 4659        );
 4660    });
 4661}
 4662
 4663#[gpui::test]
 4664async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4665    init_test(cx, |_| {});
 4666
 4667    let mut cx = EditorTestContext::new(cx).await;
 4668
 4669    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4670    cx.set_state(indoc!(
 4671        r#"abc
 4672           defˇghi
 4673
 4674           jk
 4675           nlmo
 4676           "#
 4677    ));
 4678
 4679    cx.update_editor(|editor, cx| {
 4680        editor.add_selection_above(&Default::default(), cx);
 4681    });
 4682
 4683    cx.assert_editor_state(indoc!(
 4684        r#"abcˇ
 4685           defˇghi
 4686
 4687           jk
 4688           nlmo
 4689           "#
 4690    ));
 4691
 4692    cx.update_editor(|editor, cx| {
 4693        editor.add_selection_above(&Default::default(), cx);
 4694    });
 4695
 4696    cx.assert_editor_state(indoc!(
 4697        r#"abcˇ
 4698            defˇghi
 4699
 4700            jk
 4701            nlmo
 4702            "#
 4703    ));
 4704
 4705    cx.update_editor(|view, cx| {
 4706        view.add_selection_below(&Default::default(), cx);
 4707    });
 4708
 4709    cx.assert_editor_state(indoc!(
 4710        r#"abc
 4711           defˇghi
 4712
 4713           jk
 4714           nlmo
 4715           "#
 4716    ));
 4717
 4718    cx.update_editor(|view, cx| {
 4719        view.undo_selection(&Default::default(), cx);
 4720    });
 4721
 4722    cx.assert_editor_state(indoc!(
 4723        r#"abcˇ
 4724           defˇghi
 4725
 4726           jk
 4727           nlmo
 4728           "#
 4729    ));
 4730
 4731    cx.update_editor(|view, cx| {
 4732        view.redo_selection(&Default::default(), cx);
 4733    });
 4734
 4735    cx.assert_editor_state(indoc!(
 4736        r#"abc
 4737           defˇghi
 4738
 4739           jk
 4740           nlmo
 4741           "#
 4742    ));
 4743
 4744    cx.update_editor(|view, cx| {
 4745        view.add_selection_below(&Default::default(), cx);
 4746    });
 4747
 4748    cx.assert_editor_state(indoc!(
 4749        r#"abc
 4750           defˇghi
 4751
 4752           jk
 4753           nlmˇo
 4754           "#
 4755    ));
 4756
 4757    cx.update_editor(|view, cx| {
 4758        view.add_selection_below(&Default::default(), cx);
 4759    });
 4760
 4761    cx.assert_editor_state(indoc!(
 4762        r#"abc
 4763           defˇghi
 4764
 4765           jk
 4766           nlmˇo
 4767           "#
 4768    ));
 4769
 4770    // change selections
 4771    cx.set_state(indoc!(
 4772        r#"abc
 4773           def«ˇg»hi
 4774
 4775           jk
 4776           nlmo
 4777           "#
 4778    ));
 4779
 4780    cx.update_editor(|view, cx| {
 4781        view.add_selection_below(&Default::default(), cx);
 4782    });
 4783
 4784    cx.assert_editor_state(indoc!(
 4785        r#"abc
 4786           def«ˇg»hi
 4787
 4788           jk
 4789           nlm«ˇo»
 4790           "#
 4791    ));
 4792
 4793    cx.update_editor(|view, cx| {
 4794        view.add_selection_below(&Default::default(), cx);
 4795    });
 4796
 4797    cx.assert_editor_state(indoc!(
 4798        r#"abc
 4799           def«ˇg»hi
 4800
 4801           jk
 4802           nlm«ˇo»
 4803           "#
 4804    ));
 4805
 4806    cx.update_editor(|view, cx| {
 4807        view.add_selection_above(&Default::default(), cx);
 4808    });
 4809
 4810    cx.assert_editor_state(indoc!(
 4811        r#"abc
 4812           def«ˇg»hi
 4813
 4814           jk
 4815           nlmo
 4816           "#
 4817    ));
 4818
 4819    cx.update_editor(|view, cx| {
 4820        view.add_selection_above(&Default::default(), cx);
 4821    });
 4822
 4823    cx.assert_editor_state(indoc!(
 4824        r#"abc
 4825           def«ˇg»hi
 4826
 4827           jk
 4828           nlmo
 4829           "#
 4830    ));
 4831
 4832    // Change selections again
 4833    cx.set_state(indoc!(
 4834        r#"a«bc
 4835           defgˇ»hi
 4836
 4837           jk
 4838           nlmo
 4839           "#
 4840    ));
 4841
 4842    cx.update_editor(|view, cx| {
 4843        view.add_selection_below(&Default::default(), cx);
 4844    });
 4845
 4846    cx.assert_editor_state(indoc!(
 4847        r#"a«bcˇ»
 4848           d«efgˇ»hi
 4849
 4850           j«kˇ»
 4851           nlmo
 4852           "#
 4853    ));
 4854
 4855    cx.update_editor(|view, cx| {
 4856        view.add_selection_below(&Default::default(), cx);
 4857    });
 4858    cx.assert_editor_state(indoc!(
 4859        r#"a«bcˇ»
 4860           d«efgˇ»hi
 4861
 4862           j«kˇ»
 4863           n«lmoˇ»
 4864           "#
 4865    ));
 4866    cx.update_editor(|view, cx| {
 4867        view.add_selection_above(&Default::default(), cx);
 4868    });
 4869
 4870    cx.assert_editor_state(indoc!(
 4871        r#"a«bcˇ»
 4872           d«efgˇ»hi
 4873
 4874           j«kˇ»
 4875           nlmo
 4876           "#
 4877    ));
 4878
 4879    // Change selections again
 4880    cx.set_state(indoc!(
 4881        r#"abc
 4882           d«ˇefghi
 4883
 4884           jk
 4885           nlm»o
 4886           "#
 4887    ));
 4888
 4889    cx.update_editor(|view, cx| {
 4890        view.add_selection_above(&Default::default(), cx);
 4891    });
 4892
 4893    cx.assert_editor_state(indoc!(
 4894        r#"a«ˇbc»
 4895           d«ˇef»ghi
 4896
 4897           j«ˇk»
 4898           n«ˇlm»o
 4899           "#
 4900    ));
 4901
 4902    cx.update_editor(|view, cx| {
 4903        view.add_selection_below(&Default::default(), cx);
 4904    });
 4905
 4906    cx.assert_editor_state(indoc!(
 4907        r#"abc
 4908           d«ˇef»ghi
 4909
 4910           j«ˇk»
 4911           n«ˇlm»o
 4912           "#
 4913    ));
 4914}
 4915
 4916#[gpui::test]
 4917async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4918    init_test(cx, |_| {});
 4919
 4920    let mut cx = EditorTestContext::new(cx).await;
 4921    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4922
 4923    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4924        .unwrap();
 4925    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4926
 4927    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4928        .unwrap();
 4929    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4930
 4931    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4932    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4933
 4934    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4935    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4936
 4937    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4938        .unwrap();
 4939    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4940
 4941    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4942        .unwrap();
 4943    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4944}
 4945
 4946#[gpui::test]
 4947async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4948    init_test(cx, |_| {});
 4949
 4950    let mut cx = EditorTestContext::new(cx).await;
 4951    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4952
 4953    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4954        .unwrap();
 4955    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4956}
 4957
 4958#[gpui::test]
 4959async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4960    init_test(cx, |_| {});
 4961
 4962    let mut cx = EditorTestContext::new(cx).await;
 4963    cx.set_state(
 4964        r#"let foo = 2;
 4965lˇet foo = 2;
 4966let fooˇ = 2;
 4967let foo = 2;
 4968let foo = ˇ2;"#,
 4969    );
 4970
 4971    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4972        .unwrap();
 4973    cx.assert_editor_state(
 4974        r#"let foo = 2;
 4975«letˇ» foo = 2;
 4976let «fooˇ» = 2;
 4977let foo = 2;
 4978let foo = «2ˇ»;"#,
 4979    );
 4980
 4981    // noop for multiple selections with different contents
 4982    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4983        .unwrap();
 4984    cx.assert_editor_state(
 4985        r#"let foo = 2;
 4986«letˇ» foo = 2;
 4987let «fooˇ» = 2;
 4988let foo = 2;
 4989let foo = «2ˇ»;"#,
 4990    );
 4991}
 4992
 4993#[gpui::test]
 4994async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4995    init_test(cx, |_| {});
 4996
 4997    let mut cx =
 4998        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 4999
 5000    cx.assert_editor_state(indoc! {"
 5001        ˇbbb
 5002        ccc
 5003
 5004        bbb
 5005        ccc
 5006        "});
 5007    cx.dispatch_action(SelectPrevious::default());
 5008    cx.assert_editor_state(indoc! {"
 5009                «bbbˇ»
 5010                ccc
 5011
 5012                bbb
 5013                ccc
 5014                "});
 5015    cx.dispatch_action(SelectPrevious::default());
 5016    cx.assert_editor_state(indoc! {"
 5017                «bbbˇ»
 5018                ccc
 5019
 5020                «bbbˇ»
 5021                ccc
 5022                "});
 5023}
 5024
 5025#[gpui::test]
 5026async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5027    init_test(cx, |_| {});
 5028
 5029    let mut cx = EditorTestContext::new(cx).await;
 5030    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5031
 5032    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5033        .unwrap();
 5034    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5035
 5036    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5037        .unwrap();
 5038    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5039
 5040    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5041    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5042
 5043    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5044    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5045
 5046    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5047        .unwrap();
 5048    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5049
 5050    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5051        .unwrap();
 5052    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5053
 5054    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5055        .unwrap();
 5056    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5057}
 5058
 5059#[gpui::test]
 5060async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5061    init_test(cx, |_| {});
 5062
 5063    let mut cx = EditorTestContext::new(cx).await;
 5064    cx.set_state(
 5065        r#"let foo = 2;
 5066lˇet foo = 2;
 5067let fooˇ = 2;
 5068let foo = 2;
 5069let foo = ˇ2;"#,
 5070    );
 5071
 5072    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5073        .unwrap();
 5074    cx.assert_editor_state(
 5075        r#"let foo = 2;
 5076«letˇ» foo = 2;
 5077let «fooˇ» = 2;
 5078let foo = 2;
 5079let foo = «2ˇ»;"#,
 5080    );
 5081
 5082    // noop for multiple selections with different contents
 5083    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5084        .unwrap();
 5085    cx.assert_editor_state(
 5086        r#"let foo = 2;
 5087«letˇ» foo = 2;
 5088let «fooˇ» = 2;
 5089let foo = 2;
 5090let foo = «2ˇ»;"#,
 5091    );
 5092}
 5093
 5094#[gpui::test]
 5095async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5096    init_test(cx, |_| {});
 5097
 5098    let mut cx = EditorTestContext::new(cx).await;
 5099    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5104
 5105    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5106        .unwrap();
 5107    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5108
 5109    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5110    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5111
 5112    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5113    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5114
 5115    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5118
 5119    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5120        .unwrap();
 5121    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5122}
 5123
 5124#[gpui::test]
 5125async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5126    init_test(cx, |_| {});
 5127
 5128    let language = Arc::new(Language::new(
 5129        LanguageConfig::default(),
 5130        Some(tree_sitter_rust::LANGUAGE.into()),
 5131    ));
 5132
 5133    let text = r#"
 5134        use mod1::mod2::{mod3, mod4};
 5135
 5136        fn fn_1(param1: bool, param2: &str) {
 5137            let var1 = "text";
 5138        }
 5139    "#
 5140    .unindent();
 5141
 5142    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5143    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5144    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5145
 5146    editor
 5147        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5148        .await;
 5149
 5150    editor.update(cx, |view, cx| {
 5151        view.change_selections(None, cx, |s| {
 5152            s.select_display_ranges([
 5153                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5154                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5155                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5156            ]);
 5157        });
 5158        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5159    });
 5160    editor.update(cx, |editor, cx| {
 5161        assert_text_with_selections(
 5162            editor,
 5163            indoc! {r#"
 5164                use mod1::mod2::{mod3, «mod4ˇ»};
 5165
 5166                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5167                    let var1 = "«textˇ»";
 5168                }
 5169            "#},
 5170            cx,
 5171        );
 5172    });
 5173
 5174    editor.update(cx, |view, cx| {
 5175        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5176    });
 5177    editor.update(cx, |editor, cx| {
 5178        assert_text_with_selections(
 5179            editor,
 5180            indoc! {r#"
 5181                use mod1::mod2::«{mod3, mod4}ˇ»;
 5182
 5183                «ˇfn fn_1(param1: bool, param2: &str) {
 5184                    let var1 = "text";
 5185 5186            "#},
 5187            cx,
 5188        );
 5189    });
 5190
 5191    editor.update(cx, |view, cx| {
 5192        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5193    });
 5194    assert_eq!(
 5195        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5196        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5197    );
 5198
 5199    // Trying to expand the selected syntax node one more time has no effect.
 5200    editor.update(cx, |view, cx| {
 5201        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5202    });
 5203    assert_eq!(
 5204        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5205        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5206    );
 5207
 5208    editor.update(cx, |view, cx| {
 5209        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5210    });
 5211    editor.update(cx, |editor, cx| {
 5212        assert_text_with_selections(
 5213            editor,
 5214            indoc! {r#"
 5215                use mod1::mod2::«{mod3, mod4}ˇ»;
 5216
 5217                «ˇfn fn_1(param1: bool, param2: &str) {
 5218                    let var1 = "text";
 5219 5220            "#},
 5221            cx,
 5222        );
 5223    });
 5224
 5225    editor.update(cx, |view, cx| {
 5226        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5227    });
 5228    editor.update(cx, |editor, cx| {
 5229        assert_text_with_selections(
 5230            editor,
 5231            indoc! {r#"
 5232                use mod1::mod2::{mod3, «mod4ˇ»};
 5233
 5234                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5235                    let var1 = "«textˇ»";
 5236                }
 5237            "#},
 5238            cx,
 5239        );
 5240    });
 5241
 5242    editor.update(cx, |view, cx| {
 5243        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5244    });
 5245    editor.update(cx, |editor, cx| {
 5246        assert_text_with_selections(
 5247            editor,
 5248            indoc! {r#"
 5249                use mod1::mod2::{mod3, mo«ˇ»d4};
 5250
 5251                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5252                    let var1 = "te«ˇ»xt";
 5253                }
 5254            "#},
 5255            cx,
 5256        );
 5257    });
 5258
 5259    // Trying to shrink the selected syntax node one more time has no effect.
 5260    editor.update(cx, |view, cx| {
 5261        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5262    });
 5263    editor.update(cx, |editor, cx| {
 5264        assert_text_with_selections(
 5265            editor,
 5266            indoc! {r#"
 5267                use mod1::mod2::{mod3, mo«ˇ»d4};
 5268
 5269                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5270                    let var1 = "te«ˇ»xt";
 5271                }
 5272            "#},
 5273            cx,
 5274        );
 5275    });
 5276
 5277    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5278    // a fold.
 5279    editor.update(cx, |view, cx| {
 5280        view.fold_ranges(
 5281            vec![
 5282                (
 5283                    Point::new(0, 21)..Point::new(0, 24),
 5284                    FoldPlaceholder::test(),
 5285                ),
 5286                (
 5287                    Point::new(3, 20)..Point::new(3, 22),
 5288                    FoldPlaceholder::test(),
 5289                ),
 5290            ],
 5291            true,
 5292            cx,
 5293        );
 5294        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5295    });
 5296    editor.update(cx, |editor, cx| {
 5297        assert_text_with_selections(
 5298            editor,
 5299            indoc! {r#"
 5300                use mod1::mod2::«{mod3, mod4}ˇ»;
 5301
 5302                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5303                    «let var1 = "text";ˇ»
 5304                }
 5305            "#},
 5306            cx,
 5307        );
 5308    });
 5309}
 5310
 5311#[gpui::test]
 5312async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5313    init_test(cx, |_| {});
 5314
 5315    let language = Arc::new(
 5316        Language::new(
 5317            LanguageConfig {
 5318                brackets: BracketPairConfig {
 5319                    pairs: vec![
 5320                        BracketPair {
 5321                            start: "{".to_string(),
 5322                            end: "}".to_string(),
 5323                            close: false,
 5324                            surround: false,
 5325                            newline: true,
 5326                        },
 5327                        BracketPair {
 5328                            start: "(".to_string(),
 5329                            end: ")".to_string(),
 5330                            close: false,
 5331                            surround: false,
 5332                            newline: true,
 5333                        },
 5334                    ],
 5335                    ..Default::default()
 5336                },
 5337                ..Default::default()
 5338            },
 5339            Some(tree_sitter_rust::LANGUAGE.into()),
 5340        )
 5341        .with_indents_query(
 5342            r#"
 5343                (_ "(" ")" @end) @indent
 5344                (_ "{" "}" @end) @indent
 5345            "#,
 5346        )
 5347        .unwrap(),
 5348    );
 5349
 5350    let text = "fn a() {}";
 5351
 5352    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5353    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5354    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5355    editor
 5356        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5357        .await;
 5358
 5359    editor.update(cx, |editor, cx| {
 5360        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5361        editor.newline(&Newline, cx);
 5362        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5363        assert_eq!(
 5364            editor.selections.ranges(cx),
 5365            &[
 5366                Point::new(1, 4)..Point::new(1, 4),
 5367                Point::new(3, 4)..Point::new(3, 4),
 5368                Point::new(5, 0)..Point::new(5, 0)
 5369            ]
 5370        );
 5371    });
 5372}
 5373
 5374#[gpui::test]
 5375async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5376    init_test(cx, |_| {});
 5377
 5378    let mut cx = EditorTestContext::new(cx).await;
 5379
 5380    let language = Arc::new(Language::new(
 5381        LanguageConfig {
 5382            brackets: BracketPairConfig {
 5383                pairs: vec![
 5384                    BracketPair {
 5385                        start: "{".to_string(),
 5386                        end: "}".to_string(),
 5387                        close: true,
 5388                        surround: true,
 5389                        newline: true,
 5390                    },
 5391                    BracketPair {
 5392                        start: "(".to_string(),
 5393                        end: ")".to_string(),
 5394                        close: true,
 5395                        surround: true,
 5396                        newline: true,
 5397                    },
 5398                    BracketPair {
 5399                        start: "/*".to_string(),
 5400                        end: " */".to_string(),
 5401                        close: true,
 5402                        surround: true,
 5403                        newline: true,
 5404                    },
 5405                    BracketPair {
 5406                        start: "[".to_string(),
 5407                        end: "]".to_string(),
 5408                        close: false,
 5409                        surround: false,
 5410                        newline: true,
 5411                    },
 5412                    BracketPair {
 5413                        start: "\"".to_string(),
 5414                        end: "\"".to_string(),
 5415                        close: true,
 5416                        surround: true,
 5417                        newline: false,
 5418                    },
 5419                    BracketPair {
 5420                        start: "<".to_string(),
 5421                        end: ">".to_string(),
 5422                        close: false,
 5423                        surround: true,
 5424                        newline: true,
 5425                    },
 5426                ],
 5427                ..Default::default()
 5428            },
 5429            autoclose_before: "})]".to_string(),
 5430            ..Default::default()
 5431        },
 5432        Some(tree_sitter_rust::LANGUAGE.into()),
 5433    ));
 5434
 5435    cx.language_registry().add(language.clone());
 5436    cx.update_buffer(|buffer, cx| {
 5437        buffer.set_language(Some(language), cx);
 5438    });
 5439
 5440    cx.set_state(
 5441        &r#"
 5442            🏀ˇ
 5443            εˇ
 5444            ❤️ˇ
 5445        "#
 5446        .unindent(),
 5447    );
 5448
 5449    // autoclose multiple nested brackets at multiple cursors
 5450    cx.update_editor(|view, cx| {
 5451        view.handle_input("{", cx);
 5452        view.handle_input("{", cx);
 5453        view.handle_input("{", cx);
 5454    });
 5455    cx.assert_editor_state(
 5456        &"
 5457            🏀{{{ˇ}}}
 5458            ε{{{ˇ}}}
 5459            ❤️{{{ˇ}}}
 5460        "
 5461        .unindent(),
 5462    );
 5463
 5464    // insert a different closing bracket
 5465    cx.update_editor(|view, cx| {
 5466        view.handle_input(")", cx);
 5467    });
 5468    cx.assert_editor_state(
 5469        &"
 5470            🏀{{{)ˇ}}}
 5471            ε{{{)ˇ}}}
 5472            ❤️{{{)ˇ}}}
 5473        "
 5474        .unindent(),
 5475    );
 5476
 5477    // skip over the auto-closed brackets when typing a closing bracket
 5478    cx.update_editor(|view, cx| {
 5479        view.move_right(&MoveRight, cx);
 5480        view.handle_input("}", cx);
 5481        view.handle_input("}", cx);
 5482        view.handle_input("}", cx);
 5483    });
 5484    cx.assert_editor_state(
 5485        &"
 5486            🏀{{{)}}}}ˇ
 5487            ε{{{)}}}}ˇ
 5488            ❤️{{{)}}}}ˇ
 5489        "
 5490        .unindent(),
 5491    );
 5492
 5493    // autoclose multi-character pairs
 5494    cx.set_state(
 5495        &"
 5496            ˇ
 5497            ˇ
 5498        "
 5499        .unindent(),
 5500    );
 5501    cx.update_editor(|view, cx| {
 5502        view.handle_input("/", cx);
 5503        view.handle_input("*", cx);
 5504    });
 5505    cx.assert_editor_state(
 5506        &"
 5507            /*ˇ */
 5508            /*ˇ */
 5509        "
 5510        .unindent(),
 5511    );
 5512
 5513    // one cursor autocloses a multi-character pair, one cursor
 5514    // does not autoclose.
 5515    cx.set_state(
 5516        &"
 5517 5518            ˇ
 5519        "
 5520        .unindent(),
 5521    );
 5522    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5523    cx.assert_editor_state(
 5524        &"
 5525            /*ˇ */
 5526 5527        "
 5528        .unindent(),
 5529    );
 5530
 5531    // Don't autoclose if the next character isn't whitespace and isn't
 5532    // listed in the language's "autoclose_before" section.
 5533    cx.set_state("ˇa b");
 5534    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5535    cx.assert_editor_state("{ˇa b");
 5536
 5537    // Don't autoclose if `close` is false for the bracket pair
 5538    cx.set_state("ˇ");
 5539    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5540    cx.assert_editor_state("");
 5541
 5542    // Surround with brackets if text is selected
 5543    cx.set_state("«aˇ» b");
 5544    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5545    cx.assert_editor_state("{«aˇ»} b");
 5546
 5547    // Autclose pair where the start and end characters are the same
 5548    cx.set_state("");
 5549    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5550    cx.assert_editor_state("a\"ˇ\"");
 5551    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5552    cx.assert_editor_state("a\"\"ˇ");
 5553
 5554    // Don't autoclose pair if autoclose is disabled
 5555    cx.set_state("ˇ");
 5556    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5557    cx.assert_editor_state("");
 5558
 5559    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5560    cx.set_state("«aˇ» b");
 5561    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5562    cx.assert_editor_state("<«aˇ»> b");
 5563}
 5564
 5565#[gpui::test]
 5566async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5567    init_test(cx, |settings| {
 5568        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5569    });
 5570
 5571    let mut cx = EditorTestContext::new(cx).await;
 5572
 5573    let language = Arc::new(Language::new(
 5574        LanguageConfig {
 5575            brackets: BracketPairConfig {
 5576                pairs: vec![
 5577                    BracketPair {
 5578                        start: "{".to_string(),
 5579                        end: "}".to_string(),
 5580                        close: true,
 5581                        surround: true,
 5582                        newline: true,
 5583                    },
 5584                    BracketPair {
 5585                        start: "(".to_string(),
 5586                        end: ")".to_string(),
 5587                        close: true,
 5588                        surround: true,
 5589                        newline: true,
 5590                    },
 5591                    BracketPair {
 5592                        start: "[".to_string(),
 5593                        end: "]".to_string(),
 5594                        close: false,
 5595                        surround: false,
 5596                        newline: true,
 5597                    },
 5598                ],
 5599                ..Default::default()
 5600            },
 5601            autoclose_before: "})]".to_string(),
 5602            ..Default::default()
 5603        },
 5604        Some(tree_sitter_rust::LANGUAGE.into()),
 5605    ));
 5606
 5607    cx.language_registry().add(language.clone());
 5608    cx.update_buffer(|buffer, cx| {
 5609        buffer.set_language(Some(language), cx);
 5610    });
 5611
 5612    cx.set_state(
 5613        &"
 5614            ˇ
 5615            ˇ
 5616            ˇ
 5617        "
 5618        .unindent(),
 5619    );
 5620
 5621    // ensure only matching closing brackets are skipped over
 5622    cx.update_editor(|view, cx| {
 5623        view.handle_input("}", cx);
 5624        view.move_left(&MoveLeft, cx);
 5625        view.handle_input(")", cx);
 5626        view.move_left(&MoveLeft, cx);
 5627    });
 5628    cx.assert_editor_state(
 5629        &"
 5630            ˇ)}
 5631            ˇ)}
 5632            ˇ)}
 5633        "
 5634        .unindent(),
 5635    );
 5636
 5637    // skip-over closing brackets at multiple cursors
 5638    cx.update_editor(|view, cx| {
 5639        view.handle_input(")", cx);
 5640        view.handle_input("}", cx);
 5641    });
 5642    cx.assert_editor_state(
 5643        &"
 5644            )}ˇ
 5645            )}ˇ
 5646            )}ˇ
 5647        "
 5648        .unindent(),
 5649    );
 5650
 5651    // ignore non-close brackets
 5652    cx.update_editor(|view, cx| {
 5653        view.handle_input("]", cx);
 5654        view.move_left(&MoveLeft, cx);
 5655        view.handle_input("]", cx);
 5656    });
 5657    cx.assert_editor_state(
 5658        &"
 5659            )}]ˇ]
 5660            )}]ˇ]
 5661            )}]ˇ]
 5662        "
 5663        .unindent(),
 5664    );
 5665}
 5666
 5667#[gpui::test]
 5668async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5669    init_test(cx, |_| {});
 5670
 5671    let mut cx = EditorTestContext::new(cx).await;
 5672
 5673    let html_language = Arc::new(
 5674        Language::new(
 5675            LanguageConfig {
 5676                name: "HTML".into(),
 5677                brackets: BracketPairConfig {
 5678                    pairs: vec![
 5679                        BracketPair {
 5680                            start: "<".into(),
 5681                            end: ">".into(),
 5682                            close: true,
 5683                            ..Default::default()
 5684                        },
 5685                        BracketPair {
 5686                            start: "{".into(),
 5687                            end: "}".into(),
 5688                            close: true,
 5689                            ..Default::default()
 5690                        },
 5691                        BracketPair {
 5692                            start: "(".into(),
 5693                            end: ")".into(),
 5694                            close: true,
 5695                            ..Default::default()
 5696                        },
 5697                    ],
 5698                    ..Default::default()
 5699                },
 5700                autoclose_before: "})]>".into(),
 5701                ..Default::default()
 5702            },
 5703            Some(tree_sitter_html::language()),
 5704        )
 5705        .with_injection_query(
 5706            r#"
 5707            (script_element
 5708                (raw_text) @content
 5709                (#set! "language" "javascript"))
 5710            "#,
 5711        )
 5712        .unwrap(),
 5713    );
 5714
 5715    let javascript_language = Arc::new(Language::new(
 5716        LanguageConfig {
 5717            name: "JavaScript".into(),
 5718            brackets: BracketPairConfig {
 5719                pairs: vec![
 5720                    BracketPair {
 5721                        start: "/*".into(),
 5722                        end: " */".into(),
 5723                        close: true,
 5724                        ..Default::default()
 5725                    },
 5726                    BracketPair {
 5727                        start: "{".into(),
 5728                        end: "}".into(),
 5729                        close: true,
 5730                        ..Default::default()
 5731                    },
 5732                    BracketPair {
 5733                        start: "(".into(),
 5734                        end: ")".into(),
 5735                        close: true,
 5736                        ..Default::default()
 5737                    },
 5738                ],
 5739                ..Default::default()
 5740            },
 5741            autoclose_before: "})]>".into(),
 5742            ..Default::default()
 5743        },
 5744        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5745    ));
 5746
 5747    cx.language_registry().add(html_language.clone());
 5748    cx.language_registry().add(javascript_language.clone());
 5749
 5750    cx.update_buffer(|buffer, cx| {
 5751        buffer.set_language(Some(html_language), cx);
 5752    });
 5753
 5754    cx.set_state(
 5755        &r#"
 5756            <body>ˇ
 5757                <script>
 5758                    var x = 1;ˇ
 5759                </script>
 5760            </body>ˇ
 5761        "#
 5762        .unindent(),
 5763    );
 5764
 5765    // Precondition: different languages are active at different locations.
 5766    cx.update_editor(|editor, cx| {
 5767        let snapshot = editor.snapshot(cx);
 5768        let cursors = editor.selections.ranges::<usize>(cx);
 5769        let languages = cursors
 5770            .iter()
 5771            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5772            .collect::<Vec<_>>();
 5773        assert_eq!(
 5774            languages,
 5775            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5776        );
 5777    });
 5778
 5779    // Angle brackets autoclose in HTML, but not JavaScript.
 5780    cx.update_editor(|editor, cx| {
 5781        editor.handle_input("<", cx);
 5782        editor.handle_input("a", cx);
 5783    });
 5784    cx.assert_editor_state(
 5785        &r#"
 5786            <body><aˇ>
 5787                <script>
 5788                    var x = 1;<aˇ
 5789                </script>
 5790            </body><aˇ>
 5791        "#
 5792        .unindent(),
 5793    );
 5794
 5795    // Curly braces and parens autoclose in both HTML and JavaScript.
 5796    cx.update_editor(|editor, cx| {
 5797        editor.handle_input(" b=", cx);
 5798        editor.handle_input("{", cx);
 5799        editor.handle_input("c", cx);
 5800        editor.handle_input("(", cx);
 5801    });
 5802    cx.assert_editor_state(
 5803        &r#"
 5804            <body><a b={c(ˇ)}>
 5805                <script>
 5806                    var x = 1;<a b={c(ˇ)}
 5807                </script>
 5808            </body><a b={c(ˇ)}>
 5809        "#
 5810        .unindent(),
 5811    );
 5812
 5813    // Brackets that were already autoclosed are skipped.
 5814    cx.update_editor(|editor, cx| {
 5815        editor.handle_input(")", cx);
 5816        editor.handle_input("d", cx);
 5817        editor.handle_input("}", cx);
 5818    });
 5819    cx.assert_editor_state(
 5820        &r#"
 5821            <body><a b={c()d}ˇ>
 5822                <script>
 5823                    var x = 1;<a b={c()d}ˇ
 5824                </script>
 5825            </body><a b={c()d}ˇ>
 5826        "#
 5827        .unindent(),
 5828    );
 5829    cx.update_editor(|editor, cx| {
 5830        editor.handle_input(">", cx);
 5831    });
 5832    cx.assert_editor_state(
 5833        &r#"
 5834            <body><a b={c()d}>ˇ
 5835                <script>
 5836                    var x = 1;<a b={c()d}>ˇ
 5837                </script>
 5838            </body><a b={c()d}>ˇ
 5839        "#
 5840        .unindent(),
 5841    );
 5842
 5843    // Reset
 5844    cx.set_state(
 5845        &r#"
 5846            <body>ˇ
 5847                <script>
 5848                    var x = 1;ˇ
 5849                </script>
 5850            </body>ˇ
 5851        "#
 5852        .unindent(),
 5853    );
 5854
 5855    cx.update_editor(|editor, cx| {
 5856        editor.handle_input("<", cx);
 5857    });
 5858    cx.assert_editor_state(
 5859        &r#"
 5860            <body><ˇ>
 5861                <script>
 5862                    var x = 1;<ˇ
 5863                </script>
 5864            </body><ˇ>
 5865        "#
 5866        .unindent(),
 5867    );
 5868
 5869    // When backspacing, the closing angle brackets are removed.
 5870    cx.update_editor(|editor, cx| {
 5871        editor.backspace(&Backspace, cx);
 5872    });
 5873    cx.assert_editor_state(
 5874        &r#"
 5875            <body>ˇ
 5876                <script>
 5877                    var x = 1;ˇ
 5878                </script>
 5879            </body>ˇ
 5880        "#
 5881        .unindent(),
 5882    );
 5883
 5884    // Block comments autoclose in JavaScript, but not HTML.
 5885    cx.update_editor(|editor, cx| {
 5886        editor.handle_input("/", cx);
 5887        editor.handle_input("*", cx);
 5888    });
 5889    cx.assert_editor_state(
 5890        &r#"
 5891            <body>/*ˇ
 5892                <script>
 5893                    var x = 1;/*ˇ */
 5894                </script>
 5895            </body>/*ˇ
 5896        "#
 5897        .unindent(),
 5898    );
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let mut cx = EditorTestContext::new(cx).await;
 5906
 5907    let rust_language = Arc::new(
 5908        Language::new(
 5909            LanguageConfig {
 5910                name: "Rust".into(),
 5911                brackets: serde_json::from_value(json!([
 5912                    { "start": "{", "end": "}", "close": true, "newline": true },
 5913                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5914                ]))
 5915                .unwrap(),
 5916                autoclose_before: "})]>".into(),
 5917                ..Default::default()
 5918            },
 5919            Some(tree_sitter_rust::LANGUAGE.into()),
 5920        )
 5921        .with_override_query("(string_literal) @string")
 5922        .unwrap(),
 5923    );
 5924
 5925    cx.language_registry().add(rust_language.clone());
 5926    cx.update_buffer(|buffer, cx| {
 5927        buffer.set_language(Some(rust_language), cx);
 5928    });
 5929
 5930    cx.set_state(
 5931        &r#"
 5932            let x = ˇ
 5933        "#
 5934        .unindent(),
 5935    );
 5936
 5937    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5938    cx.update_editor(|editor, cx| {
 5939        editor.handle_input("\"", cx);
 5940    });
 5941    cx.assert_editor_state(
 5942        &r#"
 5943            let x = "ˇ"
 5944        "#
 5945        .unindent(),
 5946    );
 5947
 5948    // Inserting another quotation mark. The cursor moves across the existing
 5949    // automatically-inserted quotation mark.
 5950    cx.update_editor(|editor, cx| {
 5951        editor.handle_input("\"", cx);
 5952    });
 5953    cx.assert_editor_state(
 5954        &r#"
 5955            let x = ""ˇ
 5956        "#
 5957        .unindent(),
 5958    );
 5959
 5960    // Reset
 5961    cx.set_state(
 5962        &r#"
 5963            let x = ˇ
 5964        "#
 5965        .unindent(),
 5966    );
 5967
 5968    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5969    cx.update_editor(|editor, cx| {
 5970        editor.handle_input("\"", cx);
 5971        editor.handle_input(" ", cx);
 5972        editor.move_left(&Default::default(), cx);
 5973        editor.handle_input("\\", cx);
 5974        editor.handle_input("\"", cx);
 5975    });
 5976    cx.assert_editor_state(
 5977        &r#"
 5978            let x = "\"ˇ "
 5979        "#
 5980        .unindent(),
 5981    );
 5982
 5983    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5984    // mark. Nothing is inserted.
 5985    cx.update_editor(|editor, cx| {
 5986        editor.move_right(&Default::default(), cx);
 5987        editor.handle_input("\"", cx);
 5988    });
 5989    cx.assert_editor_state(
 5990        &r#"
 5991            let x = "\" "ˇ
 5992        "#
 5993        .unindent(),
 5994    );
 5995}
 5996
 5997#[gpui::test]
 5998async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5999    init_test(cx, |_| {});
 6000
 6001    let language = Arc::new(Language::new(
 6002        LanguageConfig {
 6003            brackets: BracketPairConfig {
 6004                pairs: vec![
 6005                    BracketPair {
 6006                        start: "{".to_string(),
 6007                        end: "}".to_string(),
 6008                        close: true,
 6009                        surround: true,
 6010                        newline: true,
 6011                    },
 6012                    BracketPair {
 6013                        start: "/* ".to_string(),
 6014                        end: "*/".to_string(),
 6015                        close: true,
 6016                        surround: true,
 6017                        ..Default::default()
 6018                    },
 6019                ],
 6020                ..Default::default()
 6021            },
 6022            ..Default::default()
 6023        },
 6024        Some(tree_sitter_rust::LANGUAGE.into()),
 6025    ));
 6026
 6027    let text = r#"
 6028        a
 6029        b
 6030        c
 6031    "#
 6032    .unindent();
 6033
 6034    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6035    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6036    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6037    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6038        .await;
 6039
 6040    view.update(cx, |view, cx| {
 6041        view.change_selections(None, cx, |s| {
 6042            s.select_display_ranges([
 6043                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6044                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6045                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6046            ])
 6047        });
 6048
 6049        view.handle_input("{", cx);
 6050        view.handle_input("{", cx);
 6051        view.handle_input("{", cx);
 6052        assert_eq!(
 6053            view.text(cx),
 6054            "
 6055                {{{a}}}
 6056                {{{b}}}
 6057                {{{c}}}
 6058            "
 6059            .unindent()
 6060        );
 6061        assert_eq!(
 6062            view.selections.display_ranges(cx),
 6063            [
 6064                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6065                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6066                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6067            ]
 6068        );
 6069
 6070        view.undo(&Undo, cx);
 6071        view.undo(&Undo, cx);
 6072        view.undo(&Undo, cx);
 6073        assert_eq!(
 6074            view.text(cx),
 6075            "
 6076                a
 6077                b
 6078                c
 6079            "
 6080            .unindent()
 6081        );
 6082        assert_eq!(
 6083            view.selections.display_ranges(cx),
 6084            [
 6085                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6086                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6087                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6088            ]
 6089        );
 6090
 6091        // Ensure inserting the first character of a multi-byte bracket pair
 6092        // doesn't surround the selections with the bracket.
 6093        view.handle_input("/", cx);
 6094        assert_eq!(
 6095            view.text(cx),
 6096            "
 6097                /
 6098                /
 6099                /
 6100            "
 6101            .unindent()
 6102        );
 6103        assert_eq!(
 6104            view.selections.display_ranges(cx),
 6105            [
 6106                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6107                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6108                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6109            ]
 6110        );
 6111
 6112        view.undo(&Undo, cx);
 6113        assert_eq!(
 6114            view.text(cx),
 6115            "
 6116                a
 6117                b
 6118                c
 6119            "
 6120            .unindent()
 6121        );
 6122        assert_eq!(
 6123            view.selections.display_ranges(cx),
 6124            [
 6125                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6126                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6127                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6128            ]
 6129        );
 6130
 6131        // Ensure inserting the last character of a multi-byte bracket pair
 6132        // doesn't surround the selections with the bracket.
 6133        view.handle_input("*", cx);
 6134        assert_eq!(
 6135            view.text(cx),
 6136            "
 6137                *
 6138                *
 6139                *
 6140            "
 6141            .unindent()
 6142        );
 6143        assert_eq!(
 6144            view.selections.display_ranges(cx),
 6145            [
 6146                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6147                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6148                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6149            ]
 6150        );
 6151    });
 6152}
 6153
 6154#[gpui::test]
 6155async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6156    init_test(cx, |_| {});
 6157
 6158    let language = Arc::new(Language::new(
 6159        LanguageConfig {
 6160            brackets: BracketPairConfig {
 6161                pairs: vec![BracketPair {
 6162                    start: "{".to_string(),
 6163                    end: "}".to_string(),
 6164                    close: true,
 6165                    surround: true,
 6166                    newline: true,
 6167                }],
 6168                ..Default::default()
 6169            },
 6170            autoclose_before: "}".to_string(),
 6171            ..Default::default()
 6172        },
 6173        Some(tree_sitter_rust::LANGUAGE.into()),
 6174    ));
 6175
 6176    let text = r#"
 6177        a
 6178        b
 6179        c
 6180    "#
 6181    .unindent();
 6182
 6183    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6184    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6185    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6186    editor
 6187        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6188        .await;
 6189
 6190    editor.update(cx, |editor, cx| {
 6191        editor.change_selections(None, cx, |s| {
 6192            s.select_ranges([
 6193                Point::new(0, 1)..Point::new(0, 1),
 6194                Point::new(1, 1)..Point::new(1, 1),
 6195                Point::new(2, 1)..Point::new(2, 1),
 6196            ])
 6197        });
 6198
 6199        editor.handle_input("{", cx);
 6200        editor.handle_input("{", cx);
 6201        editor.handle_input("_", cx);
 6202        assert_eq!(
 6203            editor.text(cx),
 6204            "
 6205                a{{_}}
 6206                b{{_}}
 6207                c{{_}}
 6208            "
 6209            .unindent()
 6210        );
 6211        assert_eq!(
 6212            editor.selections.ranges::<Point>(cx),
 6213            [
 6214                Point::new(0, 4)..Point::new(0, 4),
 6215                Point::new(1, 4)..Point::new(1, 4),
 6216                Point::new(2, 4)..Point::new(2, 4)
 6217            ]
 6218        );
 6219
 6220        editor.backspace(&Default::default(), cx);
 6221        editor.backspace(&Default::default(), cx);
 6222        assert_eq!(
 6223            editor.text(cx),
 6224            "
 6225                a{}
 6226                b{}
 6227                c{}
 6228            "
 6229            .unindent()
 6230        );
 6231        assert_eq!(
 6232            editor.selections.ranges::<Point>(cx),
 6233            [
 6234                Point::new(0, 2)..Point::new(0, 2),
 6235                Point::new(1, 2)..Point::new(1, 2),
 6236                Point::new(2, 2)..Point::new(2, 2)
 6237            ]
 6238        );
 6239
 6240        editor.delete_to_previous_word_start(&Default::default(), cx);
 6241        assert_eq!(
 6242            editor.text(cx),
 6243            "
 6244                a
 6245                b
 6246                c
 6247            "
 6248            .unindent()
 6249        );
 6250        assert_eq!(
 6251            editor.selections.ranges::<Point>(cx),
 6252            [
 6253                Point::new(0, 1)..Point::new(0, 1),
 6254                Point::new(1, 1)..Point::new(1, 1),
 6255                Point::new(2, 1)..Point::new(2, 1)
 6256            ]
 6257        );
 6258    });
 6259}
 6260
 6261#[gpui::test]
 6262async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6263    init_test(cx, |settings| {
 6264        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6265    });
 6266
 6267    let mut cx = EditorTestContext::new(cx).await;
 6268
 6269    let language = Arc::new(Language::new(
 6270        LanguageConfig {
 6271            brackets: BracketPairConfig {
 6272                pairs: vec![
 6273                    BracketPair {
 6274                        start: "{".to_string(),
 6275                        end: "}".to_string(),
 6276                        close: true,
 6277                        surround: true,
 6278                        newline: true,
 6279                    },
 6280                    BracketPair {
 6281                        start: "(".to_string(),
 6282                        end: ")".to_string(),
 6283                        close: true,
 6284                        surround: true,
 6285                        newline: true,
 6286                    },
 6287                    BracketPair {
 6288                        start: "[".to_string(),
 6289                        end: "]".to_string(),
 6290                        close: false,
 6291                        surround: true,
 6292                        newline: true,
 6293                    },
 6294                ],
 6295                ..Default::default()
 6296            },
 6297            autoclose_before: "})]".to_string(),
 6298            ..Default::default()
 6299        },
 6300        Some(tree_sitter_rust::LANGUAGE.into()),
 6301    ));
 6302
 6303    cx.language_registry().add(language.clone());
 6304    cx.update_buffer(|buffer, cx| {
 6305        buffer.set_language(Some(language), cx);
 6306    });
 6307
 6308    cx.set_state(
 6309        &"
 6310            {(ˇ)}
 6311            [[ˇ]]
 6312            {(ˇ)}
 6313        "
 6314        .unindent(),
 6315    );
 6316
 6317    cx.update_editor(|view, cx| {
 6318        view.backspace(&Default::default(), cx);
 6319        view.backspace(&Default::default(), cx);
 6320    });
 6321
 6322    cx.assert_editor_state(
 6323        &"
 6324            ˇ
 6325            ˇ]]
 6326            ˇ
 6327        "
 6328        .unindent(),
 6329    );
 6330
 6331    cx.update_editor(|view, cx| {
 6332        view.handle_input("{", cx);
 6333        view.handle_input("{", cx);
 6334        view.move_right(&MoveRight, cx);
 6335        view.move_right(&MoveRight, cx);
 6336        view.move_left(&MoveLeft, cx);
 6337        view.move_left(&MoveLeft, cx);
 6338        view.backspace(&Default::default(), cx);
 6339    });
 6340
 6341    cx.assert_editor_state(
 6342        &"
 6343            {ˇ}
 6344            {ˇ}]]
 6345            {ˇ}
 6346        "
 6347        .unindent(),
 6348    );
 6349
 6350    cx.update_editor(|view, cx| {
 6351        view.backspace(&Default::default(), cx);
 6352    });
 6353
 6354    cx.assert_editor_state(
 6355        &"
 6356            ˇ
 6357            ˇ]]
 6358            ˇ
 6359        "
 6360        .unindent(),
 6361    );
 6362}
 6363
 6364#[gpui::test]
 6365async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6366    init_test(cx, |_| {});
 6367
 6368    let language = Arc::new(Language::new(
 6369        LanguageConfig::default(),
 6370        Some(tree_sitter_rust::LANGUAGE.into()),
 6371    ));
 6372
 6373    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6374    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6375    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6376    editor
 6377        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6378        .await;
 6379
 6380    editor.update(cx, |editor, cx| {
 6381        editor.set_auto_replace_emoji_shortcode(true);
 6382
 6383        editor.handle_input("Hello ", cx);
 6384        editor.handle_input(":wave", cx);
 6385        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6386
 6387        editor.handle_input(":", cx);
 6388        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6389
 6390        editor.handle_input(" :smile", cx);
 6391        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6392
 6393        editor.handle_input(":", cx);
 6394        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6395
 6396        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6397        editor.handle_input(":wave", cx);
 6398        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6399
 6400        editor.handle_input(":", cx);
 6401        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6402
 6403        editor.handle_input(":1", cx);
 6404        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6405
 6406        editor.handle_input(":", cx);
 6407        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6408
 6409        // Ensure shortcode does not get replaced when it is part of a word
 6410        editor.handle_input(" Test:wave", cx);
 6411        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6412
 6413        editor.handle_input(":", cx);
 6414        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6415
 6416        editor.set_auto_replace_emoji_shortcode(false);
 6417
 6418        // Ensure shortcode does not get replaced when auto replace is off
 6419        editor.handle_input(" :wave", cx);
 6420        assert_eq!(
 6421            editor.text(cx),
 6422            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6423        );
 6424
 6425        editor.handle_input(":", cx);
 6426        assert_eq!(
 6427            editor.text(cx),
 6428            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6429        );
 6430    });
 6431}
 6432
 6433#[gpui::test]
 6434async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6435    init_test(cx, |_| {});
 6436
 6437    let (text, insertion_ranges) = marked_text_ranges(
 6438        indoc! {"
 6439            a.ˇ b
 6440            a.ˇ b
 6441            a.ˇ b
 6442        "},
 6443        false,
 6444    );
 6445
 6446    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6447    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6448
 6449    editor.update(cx, |editor, cx| {
 6450        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6451
 6452        editor
 6453            .insert_snippet(&insertion_ranges, snippet, cx)
 6454            .unwrap();
 6455
 6456        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6457            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6458            assert_eq!(editor.text(cx), expected_text);
 6459            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6460        }
 6461
 6462        assert(
 6463            editor,
 6464            cx,
 6465            indoc! {"
 6466                a.f(«one», two, «three») b
 6467                a.f(«one», two, «three») b
 6468                a.f(«one», two, «three») b
 6469            "},
 6470        );
 6471
 6472        // Can't move earlier than the first tab stop
 6473        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6474        assert(
 6475            editor,
 6476            cx,
 6477            indoc! {"
 6478                a.f(«one», two, «three») b
 6479                a.f(«one», two, «three») b
 6480                a.f(«one», two, «three») b
 6481            "},
 6482        );
 6483
 6484        assert!(editor.move_to_next_snippet_tabstop(cx));
 6485        assert(
 6486            editor,
 6487            cx,
 6488            indoc! {"
 6489                a.f(one, «two», three) b
 6490                a.f(one, «two», three) b
 6491                a.f(one, «two», three) b
 6492            "},
 6493        );
 6494
 6495        editor.move_to_prev_snippet_tabstop(cx);
 6496        assert(
 6497            editor,
 6498            cx,
 6499            indoc! {"
 6500                a.f(«one», two, «three») b
 6501                a.f(«one», two, «three») b
 6502                a.f(«one», two, «three») b
 6503            "},
 6504        );
 6505
 6506        assert!(editor.move_to_next_snippet_tabstop(cx));
 6507        assert(
 6508            editor,
 6509            cx,
 6510            indoc! {"
 6511                a.f(one, «two», three) b
 6512                a.f(one, «two», three) b
 6513                a.f(one, «two», three) b
 6514            "},
 6515        );
 6516        assert!(editor.move_to_next_snippet_tabstop(cx));
 6517        assert(
 6518            editor,
 6519            cx,
 6520            indoc! {"
 6521                a.f(one, two, three)ˇ b
 6522                a.f(one, two, three)ˇ b
 6523                a.f(one, two, three)ˇ b
 6524            "},
 6525        );
 6526
 6527        // As soon as the last tab stop is reached, snippet state is gone
 6528        editor.move_to_prev_snippet_tabstop(cx);
 6529        assert(
 6530            editor,
 6531            cx,
 6532            indoc! {"
 6533                a.f(one, two, three)ˇ b
 6534                a.f(one, two, three)ˇ b
 6535                a.f(one, two, three)ˇ b
 6536            "},
 6537        );
 6538    });
 6539}
 6540
 6541#[gpui::test]
 6542async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6543    init_test(cx, |_| {});
 6544
 6545    let fs = FakeFs::new(cx.executor());
 6546    fs.insert_file("/file.rs", Default::default()).await;
 6547
 6548    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6549
 6550    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6551    language_registry.add(rust_lang());
 6552    let mut fake_servers = language_registry.register_fake_lsp(
 6553        "Rust",
 6554        FakeLspAdapter {
 6555            capabilities: lsp::ServerCapabilities {
 6556                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6557                ..Default::default()
 6558            },
 6559            ..Default::default()
 6560        },
 6561    );
 6562
 6563    let buffer = project
 6564        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6565        .await
 6566        .unwrap();
 6567
 6568    cx.executor().start_waiting();
 6569    let fake_server = fake_servers.next().await.unwrap();
 6570
 6571    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6572    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6573    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6574    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6575
 6576    let save = editor
 6577        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6578        .unwrap();
 6579    fake_server
 6580        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6581            assert_eq!(
 6582                params.text_document.uri,
 6583                lsp::Url::from_file_path("/file.rs").unwrap()
 6584            );
 6585            assert_eq!(params.options.tab_size, 4);
 6586            Ok(Some(vec![lsp::TextEdit::new(
 6587                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6588                ", ".to_string(),
 6589            )]))
 6590        })
 6591        .next()
 6592        .await;
 6593    cx.executor().start_waiting();
 6594    save.await;
 6595
 6596    assert_eq!(
 6597        editor.update(cx, |editor, cx| editor.text(cx)),
 6598        "one, two\nthree\n"
 6599    );
 6600    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6601
 6602    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6603    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6604
 6605    // Ensure we can still save even if formatting hangs.
 6606    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6607        assert_eq!(
 6608            params.text_document.uri,
 6609            lsp::Url::from_file_path("/file.rs").unwrap()
 6610        );
 6611        futures::future::pending::<()>().await;
 6612        unreachable!()
 6613    });
 6614    let save = editor
 6615        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6616        .unwrap();
 6617    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6618    cx.executor().start_waiting();
 6619    save.await;
 6620    assert_eq!(
 6621        editor.update(cx, |editor, cx| editor.text(cx)),
 6622        "one\ntwo\nthree\n"
 6623    );
 6624    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6625
 6626    // For non-dirty buffer, no formatting request should be sent
 6627    let save = editor
 6628        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6629        .unwrap();
 6630    let _pending_format_request = fake_server
 6631        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6632            panic!("Should not be invoked on non-dirty buffer");
 6633        })
 6634        .next();
 6635    cx.executor().start_waiting();
 6636    save.await;
 6637
 6638    // Set rust language override and assert overridden tabsize is sent to language server
 6639    update_test_language_settings(cx, |settings| {
 6640        settings.languages.insert(
 6641            "Rust".into(),
 6642            LanguageSettingsContent {
 6643                tab_size: NonZeroU32::new(8),
 6644                ..Default::default()
 6645            },
 6646        );
 6647    });
 6648
 6649    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6650    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6651    let save = editor
 6652        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6653        .unwrap();
 6654    fake_server
 6655        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6656            assert_eq!(
 6657                params.text_document.uri,
 6658                lsp::Url::from_file_path("/file.rs").unwrap()
 6659            );
 6660            assert_eq!(params.options.tab_size, 8);
 6661            Ok(Some(vec![]))
 6662        })
 6663        .next()
 6664        .await;
 6665    cx.executor().start_waiting();
 6666    save.await;
 6667}
 6668
 6669#[gpui::test]
 6670async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6671    init_test(cx, |_| {});
 6672
 6673    let cols = 4;
 6674    let rows = 10;
 6675    let sample_text_1 = sample_text(rows, cols, 'a');
 6676    assert_eq!(
 6677        sample_text_1,
 6678        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6679    );
 6680    let sample_text_2 = sample_text(rows, cols, 'l');
 6681    assert_eq!(
 6682        sample_text_2,
 6683        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6684    );
 6685    let sample_text_3 = sample_text(rows, cols, 'v');
 6686    assert_eq!(
 6687        sample_text_3,
 6688        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6689    );
 6690
 6691    let fs = FakeFs::new(cx.executor());
 6692    fs.insert_tree(
 6693        "/a",
 6694        json!({
 6695            "main.rs": sample_text_1,
 6696            "other.rs": sample_text_2,
 6697            "lib.rs": sample_text_3,
 6698        }),
 6699    )
 6700    .await;
 6701
 6702    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6703    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6704    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6705
 6706    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6707    language_registry.add(rust_lang());
 6708    let mut fake_servers = language_registry.register_fake_lsp(
 6709        "Rust",
 6710        FakeLspAdapter {
 6711            capabilities: lsp::ServerCapabilities {
 6712                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6713                ..Default::default()
 6714            },
 6715            ..Default::default()
 6716        },
 6717    );
 6718
 6719    let worktree = project.update(cx, |project, cx| {
 6720        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6721        assert_eq!(worktrees.len(), 1);
 6722        worktrees.pop().unwrap()
 6723    });
 6724    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6725
 6726    let buffer_1 = project
 6727        .update(cx, |project, cx| {
 6728            project.open_buffer((worktree_id, "main.rs"), cx)
 6729        })
 6730        .await
 6731        .unwrap();
 6732    let buffer_2 = project
 6733        .update(cx, |project, cx| {
 6734            project.open_buffer((worktree_id, "other.rs"), cx)
 6735        })
 6736        .await
 6737        .unwrap();
 6738    let buffer_3 = project
 6739        .update(cx, |project, cx| {
 6740            project.open_buffer((worktree_id, "lib.rs"), cx)
 6741        })
 6742        .await
 6743        .unwrap();
 6744
 6745    let multi_buffer = cx.new_model(|cx| {
 6746        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6747        multi_buffer.push_excerpts(
 6748            buffer_1.clone(),
 6749            [
 6750                ExcerptRange {
 6751                    context: Point::new(0, 0)..Point::new(3, 0),
 6752                    primary: None,
 6753                },
 6754                ExcerptRange {
 6755                    context: Point::new(5, 0)..Point::new(7, 0),
 6756                    primary: None,
 6757                },
 6758                ExcerptRange {
 6759                    context: Point::new(9, 0)..Point::new(10, 4),
 6760                    primary: None,
 6761                },
 6762            ],
 6763            cx,
 6764        );
 6765        multi_buffer.push_excerpts(
 6766            buffer_2.clone(),
 6767            [
 6768                ExcerptRange {
 6769                    context: Point::new(0, 0)..Point::new(3, 0),
 6770                    primary: None,
 6771                },
 6772                ExcerptRange {
 6773                    context: Point::new(5, 0)..Point::new(7, 0),
 6774                    primary: None,
 6775                },
 6776                ExcerptRange {
 6777                    context: Point::new(9, 0)..Point::new(10, 4),
 6778                    primary: None,
 6779                },
 6780            ],
 6781            cx,
 6782        );
 6783        multi_buffer.push_excerpts(
 6784            buffer_3.clone(),
 6785            [
 6786                ExcerptRange {
 6787                    context: Point::new(0, 0)..Point::new(3, 0),
 6788                    primary: None,
 6789                },
 6790                ExcerptRange {
 6791                    context: Point::new(5, 0)..Point::new(7, 0),
 6792                    primary: None,
 6793                },
 6794                ExcerptRange {
 6795                    context: Point::new(9, 0)..Point::new(10, 4),
 6796                    primary: None,
 6797                },
 6798            ],
 6799            cx,
 6800        );
 6801        multi_buffer
 6802    });
 6803    let multi_buffer_editor = cx.new_view(|cx| {
 6804        Editor::new(
 6805            EditorMode::Full,
 6806            multi_buffer,
 6807            Some(project.clone()),
 6808            true,
 6809            cx,
 6810        )
 6811    });
 6812
 6813    multi_buffer_editor.update(cx, |editor, cx| {
 6814        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6815        editor.insert("|one|two|three|", cx);
 6816    });
 6817    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6818    multi_buffer_editor.update(cx, |editor, cx| {
 6819        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6820            s.select_ranges(Some(60..70))
 6821        });
 6822        editor.insert("|four|five|six|", cx);
 6823    });
 6824    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6825
 6826    // First two buffers should be edited, but not the third one.
 6827    assert_eq!(
 6828        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6829        "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}",
 6830    );
 6831    buffer_1.update(cx, |buffer, _| {
 6832        assert!(buffer.is_dirty());
 6833        assert_eq!(
 6834            buffer.text(),
 6835            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6836        )
 6837    });
 6838    buffer_2.update(cx, |buffer, _| {
 6839        assert!(buffer.is_dirty());
 6840        assert_eq!(
 6841            buffer.text(),
 6842            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6843        )
 6844    });
 6845    buffer_3.update(cx, |buffer, _| {
 6846        assert!(!buffer.is_dirty());
 6847        assert_eq!(buffer.text(), sample_text_3,)
 6848    });
 6849
 6850    cx.executor().start_waiting();
 6851    let save = multi_buffer_editor
 6852        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6853        .unwrap();
 6854
 6855    let fake_server = fake_servers.next().await.unwrap();
 6856    fake_server
 6857        .server
 6858        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6859            Ok(Some(vec![lsp::TextEdit::new(
 6860                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6861                format!("[{} formatted]", params.text_document.uri),
 6862            )]))
 6863        })
 6864        .detach();
 6865    save.await;
 6866
 6867    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6868    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6869    assert_eq!(
 6870        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6871        "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}",
 6872    );
 6873    buffer_1.update(cx, |buffer, _| {
 6874        assert!(!buffer.is_dirty());
 6875        assert_eq!(
 6876            buffer.text(),
 6877            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6878        )
 6879    });
 6880    buffer_2.update(cx, |buffer, _| {
 6881        assert!(!buffer.is_dirty());
 6882        assert_eq!(
 6883            buffer.text(),
 6884            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6885        )
 6886    });
 6887    buffer_3.update(cx, |buffer, _| {
 6888        assert!(!buffer.is_dirty());
 6889        assert_eq!(buffer.text(), sample_text_3,)
 6890    });
 6891}
 6892
 6893#[gpui::test]
 6894async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6895    init_test(cx, |_| {});
 6896
 6897    let fs = FakeFs::new(cx.executor());
 6898    fs.insert_file("/file.rs", Default::default()).await;
 6899
 6900    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6901
 6902    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6903    language_registry.add(rust_lang());
 6904    let mut fake_servers = language_registry.register_fake_lsp(
 6905        "Rust",
 6906        FakeLspAdapter {
 6907            capabilities: lsp::ServerCapabilities {
 6908                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6909                ..Default::default()
 6910            },
 6911            ..Default::default()
 6912        },
 6913    );
 6914
 6915    let buffer = project
 6916        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6917        .await
 6918        .unwrap();
 6919
 6920    cx.executor().start_waiting();
 6921    let fake_server = fake_servers.next().await.unwrap();
 6922
 6923    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6924    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6925    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6926    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6927
 6928    let save = editor
 6929        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6930        .unwrap();
 6931    fake_server
 6932        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6933            assert_eq!(
 6934                params.text_document.uri,
 6935                lsp::Url::from_file_path("/file.rs").unwrap()
 6936            );
 6937            assert_eq!(params.options.tab_size, 4);
 6938            Ok(Some(vec![lsp::TextEdit::new(
 6939                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6940                ", ".to_string(),
 6941            )]))
 6942        })
 6943        .next()
 6944        .await;
 6945    cx.executor().start_waiting();
 6946    save.await;
 6947    assert_eq!(
 6948        editor.update(cx, |editor, cx| editor.text(cx)),
 6949        "one, two\nthree\n"
 6950    );
 6951    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6952
 6953    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6954    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6955
 6956    // Ensure we can still save even if formatting hangs.
 6957    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6958        move |params, _| async move {
 6959            assert_eq!(
 6960                params.text_document.uri,
 6961                lsp::Url::from_file_path("/file.rs").unwrap()
 6962            );
 6963            futures::future::pending::<()>().await;
 6964            unreachable!()
 6965        },
 6966    );
 6967    let save = editor
 6968        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6969        .unwrap();
 6970    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6971    cx.executor().start_waiting();
 6972    save.await;
 6973    assert_eq!(
 6974        editor.update(cx, |editor, cx| editor.text(cx)),
 6975        "one\ntwo\nthree\n"
 6976    );
 6977    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6978
 6979    // For non-dirty buffer, no formatting request should be sent
 6980    let save = editor
 6981        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6982        .unwrap();
 6983    let _pending_format_request = fake_server
 6984        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6985            panic!("Should not be invoked on non-dirty buffer");
 6986        })
 6987        .next();
 6988    cx.executor().start_waiting();
 6989    save.await;
 6990
 6991    // Set Rust language override and assert overridden tabsize is sent to language server
 6992    update_test_language_settings(cx, |settings| {
 6993        settings.languages.insert(
 6994            "Rust".into(),
 6995            LanguageSettingsContent {
 6996                tab_size: NonZeroU32::new(8),
 6997                ..Default::default()
 6998            },
 6999        );
 7000    });
 7001
 7002    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7003    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7004    let save = editor
 7005        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7006        .unwrap();
 7007    fake_server
 7008        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7009            assert_eq!(
 7010                params.text_document.uri,
 7011                lsp::Url::from_file_path("/file.rs").unwrap()
 7012            );
 7013            assert_eq!(params.options.tab_size, 8);
 7014            Ok(Some(vec![]))
 7015        })
 7016        .next()
 7017        .await;
 7018    cx.executor().start_waiting();
 7019    save.await;
 7020}
 7021
 7022#[gpui::test]
 7023async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7024    init_test(cx, |settings| {
 7025        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7026            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7027        ))
 7028    });
 7029
 7030    let fs = FakeFs::new(cx.executor());
 7031    fs.insert_file("/file.rs", Default::default()).await;
 7032
 7033    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7034
 7035    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7036    language_registry.add(Arc::new(Language::new(
 7037        LanguageConfig {
 7038            name: "Rust".into(),
 7039            matcher: LanguageMatcher {
 7040                path_suffixes: vec!["rs".to_string()],
 7041                ..Default::default()
 7042            },
 7043            ..LanguageConfig::default()
 7044        },
 7045        Some(tree_sitter_rust::LANGUAGE.into()),
 7046    )));
 7047    update_test_language_settings(cx, |settings| {
 7048        // Enable Prettier formatting for the same buffer, and ensure
 7049        // LSP is called instead of Prettier.
 7050        settings.defaults.prettier = Some(PrettierSettings {
 7051            allowed: true,
 7052            ..PrettierSettings::default()
 7053        });
 7054    });
 7055    let mut fake_servers = language_registry.register_fake_lsp(
 7056        "Rust",
 7057        FakeLspAdapter {
 7058            capabilities: lsp::ServerCapabilities {
 7059                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7060                ..Default::default()
 7061            },
 7062            ..Default::default()
 7063        },
 7064    );
 7065
 7066    let buffer = project
 7067        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7068        .await
 7069        .unwrap();
 7070
 7071    cx.executor().start_waiting();
 7072    let fake_server = fake_servers.next().await.unwrap();
 7073
 7074    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7075    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7076    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7077
 7078    let format = editor
 7079        .update(cx, |editor, cx| {
 7080            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 7081        })
 7082        .unwrap();
 7083    fake_server
 7084        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7085            assert_eq!(
 7086                params.text_document.uri,
 7087                lsp::Url::from_file_path("/file.rs").unwrap()
 7088            );
 7089            assert_eq!(params.options.tab_size, 4);
 7090            Ok(Some(vec![lsp::TextEdit::new(
 7091                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7092                ", ".to_string(),
 7093            )]))
 7094        })
 7095        .next()
 7096        .await;
 7097    cx.executor().start_waiting();
 7098    format.await;
 7099    assert_eq!(
 7100        editor.update(cx, |editor, cx| editor.text(cx)),
 7101        "one, two\nthree\n"
 7102    );
 7103
 7104    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7105    // Ensure we don't lock if formatting hangs.
 7106    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7107        assert_eq!(
 7108            params.text_document.uri,
 7109            lsp::Url::from_file_path("/file.rs").unwrap()
 7110        );
 7111        futures::future::pending::<()>().await;
 7112        unreachable!()
 7113    });
 7114    let format = editor
 7115        .update(cx, |editor, cx| {
 7116            editor.perform_format(project, FormatTrigger::Manual, cx)
 7117        })
 7118        .unwrap();
 7119    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7120    cx.executor().start_waiting();
 7121    format.await;
 7122    assert_eq!(
 7123        editor.update(cx, |editor, cx| editor.text(cx)),
 7124        "one\ntwo\nthree\n"
 7125    );
 7126}
 7127
 7128#[gpui::test]
 7129async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7130    init_test(cx, |_| {});
 7131
 7132    let mut cx = EditorLspTestContext::new_rust(
 7133        lsp::ServerCapabilities {
 7134            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7135            ..Default::default()
 7136        },
 7137        cx,
 7138    )
 7139    .await;
 7140
 7141    cx.set_state(indoc! {"
 7142        one.twoˇ
 7143    "});
 7144
 7145    // The format request takes a long time. When it completes, it inserts
 7146    // a newline and an indent before the `.`
 7147    cx.lsp
 7148        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7149            let executor = cx.background_executor().clone();
 7150            async move {
 7151                executor.timer(Duration::from_millis(100)).await;
 7152                Ok(Some(vec![lsp::TextEdit {
 7153                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7154                    new_text: "\n    ".into(),
 7155                }]))
 7156            }
 7157        });
 7158
 7159    // Submit a format request.
 7160    let format_1 = cx
 7161        .update_editor(|editor, cx| editor.format(&Format, cx))
 7162        .unwrap();
 7163    cx.executor().run_until_parked();
 7164
 7165    // Submit a second format request.
 7166    let format_2 = cx
 7167        .update_editor(|editor, cx| editor.format(&Format, cx))
 7168        .unwrap();
 7169    cx.executor().run_until_parked();
 7170
 7171    // Wait for both format requests to complete
 7172    cx.executor().advance_clock(Duration::from_millis(200));
 7173    cx.executor().start_waiting();
 7174    format_1.await.unwrap();
 7175    cx.executor().start_waiting();
 7176    format_2.await.unwrap();
 7177
 7178    // The formatting edits only happens once.
 7179    cx.assert_editor_state(indoc! {"
 7180        one
 7181            .twoˇ
 7182    "});
 7183}
 7184
 7185#[gpui::test]
 7186async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7187    init_test(cx, |settings| {
 7188        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7189    });
 7190
 7191    let mut cx = EditorLspTestContext::new_rust(
 7192        lsp::ServerCapabilities {
 7193            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7194            ..Default::default()
 7195        },
 7196        cx,
 7197    )
 7198    .await;
 7199
 7200    // Set up a buffer white some trailing whitespace and no trailing newline.
 7201    cx.set_state(
 7202        &[
 7203            "one ",   //
 7204            "twoˇ",   //
 7205            "three ", //
 7206            "four",   //
 7207        ]
 7208        .join("\n"),
 7209    );
 7210
 7211    // Submit a format request.
 7212    let format = cx
 7213        .update_editor(|editor, cx| editor.format(&Format, cx))
 7214        .unwrap();
 7215
 7216    // Record which buffer changes have been sent to the language server
 7217    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7218    cx.lsp
 7219        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7220            let buffer_changes = buffer_changes.clone();
 7221            move |params, _| {
 7222                buffer_changes.lock().extend(
 7223                    params
 7224                        .content_changes
 7225                        .into_iter()
 7226                        .map(|e| (e.range.unwrap(), e.text)),
 7227                );
 7228            }
 7229        });
 7230
 7231    // Handle formatting requests to the language server.
 7232    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7233        let buffer_changes = buffer_changes.clone();
 7234        move |_, _| {
 7235            // When formatting is requested, trailing whitespace has already been stripped,
 7236            // and the trailing newline has already been added.
 7237            assert_eq!(
 7238                &buffer_changes.lock()[1..],
 7239                &[
 7240                    (
 7241                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7242                        "".into()
 7243                    ),
 7244                    (
 7245                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7246                        "".into()
 7247                    ),
 7248                    (
 7249                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7250                        "\n".into()
 7251                    ),
 7252                ]
 7253            );
 7254
 7255            // Insert blank lines between each line of the buffer.
 7256            async move {
 7257                Ok(Some(vec![
 7258                    lsp::TextEdit {
 7259                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7260                        new_text: "\n".into(),
 7261                    },
 7262                    lsp::TextEdit {
 7263                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7264                        new_text: "\n".into(),
 7265                    },
 7266                ]))
 7267            }
 7268        }
 7269    });
 7270
 7271    // After formatting the buffer, the trailing whitespace is stripped,
 7272    // a newline is appended, and the edits provided by the language server
 7273    // have been applied.
 7274    format.await.unwrap();
 7275    cx.assert_editor_state(
 7276        &[
 7277            "one",   //
 7278            "",      //
 7279            "twoˇ",  //
 7280            "",      //
 7281            "three", //
 7282            "four",  //
 7283            "",      //
 7284        ]
 7285        .join("\n"),
 7286    );
 7287
 7288    // Undoing the formatting undoes the trailing whitespace removal, the
 7289    // trailing newline, and the LSP edits.
 7290    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7291    cx.assert_editor_state(
 7292        &[
 7293            "one ",   //
 7294            "twoˇ",   //
 7295            "three ", //
 7296            "four",   //
 7297        ]
 7298        .join("\n"),
 7299    );
 7300}
 7301
 7302#[gpui::test]
 7303async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7304    cx: &mut gpui::TestAppContext,
 7305) {
 7306    init_test(cx, |_| {});
 7307
 7308    cx.update(|cx| {
 7309        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7310            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7311                settings.auto_signature_help = Some(true);
 7312            });
 7313        });
 7314    });
 7315
 7316    let mut cx = EditorLspTestContext::new_rust(
 7317        lsp::ServerCapabilities {
 7318            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7319                ..Default::default()
 7320            }),
 7321            ..Default::default()
 7322        },
 7323        cx,
 7324    )
 7325    .await;
 7326
 7327    let language = Language::new(
 7328        LanguageConfig {
 7329            name: "Rust".into(),
 7330            brackets: BracketPairConfig {
 7331                pairs: vec![
 7332                    BracketPair {
 7333                        start: "{".to_string(),
 7334                        end: "}".to_string(),
 7335                        close: true,
 7336                        surround: true,
 7337                        newline: true,
 7338                    },
 7339                    BracketPair {
 7340                        start: "(".to_string(),
 7341                        end: ")".to_string(),
 7342                        close: true,
 7343                        surround: true,
 7344                        newline: true,
 7345                    },
 7346                    BracketPair {
 7347                        start: "/*".to_string(),
 7348                        end: " */".to_string(),
 7349                        close: true,
 7350                        surround: true,
 7351                        newline: true,
 7352                    },
 7353                    BracketPair {
 7354                        start: "[".to_string(),
 7355                        end: "]".to_string(),
 7356                        close: false,
 7357                        surround: false,
 7358                        newline: true,
 7359                    },
 7360                    BracketPair {
 7361                        start: "\"".to_string(),
 7362                        end: "\"".to_string(),
 7363                        close: true,
 7364                        surround: true,
 7365                        newline: false,
 7366                    },
 7367                    BracketPair {
 7368                        start: "<".to_string(),
 7369                        end: ">".to_string(),
 7370                        close: false,
 7371                        surround: true,
 7372                        newline: true,
 7373                    },
 7374                ],
 7375                ..Default::default()
 7376            },
 7377            autoclose_before: "})]".to_string(),
 7378            ..Default::default()
 7379        },
 7380        Some(tree_sitter_rust::LANGUAGE.into()),
 7381    );
 7382    let language = Arc::new(language);
 7383
 7384    cx.language_registry().add(language.clone());
 7385    cx.update_buffer(|buffer, cx| {
 7386        buffer.set_language(Some(language), cx);
 7387    });
 7388
 7389    cx.set_state(
 7390        &r#"
 7391            fn main() {
 7392                sampleˇ
 7393            }
 7394        "#
 7395        .unindent(),
 7396    );
 7397
 7398    cx.update_editor(|view, cx| {
 7399        view.handle_input("(", cx);
 7400    });
 7401    cx.assert_editor_state(
 7402        &"
 7403            fn main() {
 7404                sample(ˇ)
 7405            }
 7406        "
 7407        .unindent(),
 7408    );
 7409
 7410    let mocked_response = lsp::SignatureHelp {
 7411        signatures: vec![lsp::SignatureInformation {
 7412            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7413            documentation: None,
 7414            parameters: Some(vec![
 7415                lsp::ParameterInformation {
 7416                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7417                    documentation: None,
 7418                },
 7419                lsp::ParameterInformation {
 7420                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7421                    documentation: None,
 7422                },
 7423            ]),
 7424            active_parameter: None,
 7425        }],
 7426        active_signature: Some(0),
 7427        active_parameter: Some(0),
 7428    };
 7429    handle_signature_help_request(&mut cx, mocked_response).await;
 7430
 7431    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7432        .await;
 7433
 7434    cx.editor(|editor, _| {
 7435        let signature_help_state = editor.signature_help_state.popover().cloned();
 7436        assert!(signature_help_state.is_some());
 7437        let ParsedMarkdown {
 7438            text, highlights, ..
 7439        } = signature_help_state.unwrap().parsed_content;
 7440        assert_eq!(text, "param1: u8, param2: u8");
 7441        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7442    });
 7443}
 7444
 7445#[gpui::test]
 7446async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7447    init_test(cx, |_| {});
 7448
 7449    cx.update(|cx| {
 7450        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7451            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7452                settings.auto_signature_help = Some(false);
 7453                settings.show_signature_help_after_edits = Some(false);
 7454            });
 7455        });
 7456    });
 7457
 7458    let mut cx = EditorLspTestContext::new_rust(
 7459        lsp::ServerCapabilities {
 7460            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7461                ..Default::default()
 7462            }),
 7463            ..Default::default()
 7464        },
 7465        cx,
 7466    )
 7467    .await;
 7468
 7469    let language = Language::new(
 7470        LanguageConfig {
 7471            name: "Rust".into(),
 7472            brackets: BracketPairConfig {
 7473                pairs: vec![
 7474                    BracketPair {
 7475                        start: "{".to_string(),
 7476                        end: "}".to_string(),
 7477                        close: true,
 7478                        surround: true,
 7479                        newline: true,
 7480                    },
 7481                    BracketPair {
 7482                        start: "(".to_string(),
 7483                        end: ")".to_string(),
 7484                        close: true,
 7485                        surround: true,
 7486                        newline: true,
 7487                    },
 7488                    BracketPair {
 7489                        start: "/*".to_string(),
 7490                        end: " */".to_string(),
 7491                        close: true,
 7492                        surround: true,
 7493                        newline: true,
 7494                    },
 7495                    BracketPair {
 7496                        start: "[".to_string(),
 7497                        end: "]".to_string(),
 7498                        close: false,
 7499                        surround: false,
 7500                        newline: true,
 7501                    },
 7502                    BracketPair {
 7503                        start: "\"".to_string(),
 7504                        end: "\"".to_string(),
 7505                        close: true,
 7506                        surround: true,
 7507                        newline: false,
 7508                    },
 7509                    BracketPair {
 7510                        start: "<".to_string(),
 7511                        end: ">".to_string(),
 7512                        close: false,
 7513                        surround: true,
 7514                        newline: true,
 7515                    },
 7516                ],
 7517                ..Default::default()
 7518            },
 7519            autoclose_before: "})]".to_string(),
 7520            ..Default::default()
 7521        },
 7522        Some(tree_sitter_rust::LANGUAGE.into()),
 7523    );
 7524    let language = Arc::new(language);
 7525
 7526    cx.language_registry().add(language.clone());
 7527    cx.update_buffer(|buffer, cx| {
 7528        buffer.set_language(Some(language), cx);
 7529    });
 7530
 7531    // Ensure that signature_help is not called when no signature help is enabled.
 7532    cx.set_state(
 7533        &r#"
 7534            fn main() {
 7535                sampleˇ
 7536            }
 7537        "#
 7538        .unindent(),
 7539    );
 7540    cx.update_editor(|view, cx| {
 7541        view.handle_input("(", cx);
 7542    });
 7543    cx.assert_editor_state(
 7544        &"
 7545            fn main() {
 7546                sample(ˇ)
 7547            }
 7548        "
 7549        .unindent(),
 7550    );
 7551    cx.editor(|editor, _| {
 7552        assert!(editor.signature_help_state.task().is_none());
 7553    });
 7554
 7555    let mocked_response = lsp::SignatureHelp {
 7556        signatures: vec![lsp::SignatureInformation {
 7557            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7558            documentation: None,
 7559            parameters: Some(vec![
 7560                lsp::ParameterInformation {
 7561                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7562                    documentation: None,
 7563                },
 7564                lsp::ParameterInformation {
 7565                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7566                    documentation: None,
 7567                },
 7568            ]),
 7569            active_parameter: None,
 7570        }],
 7571        active_signature: Some(0),
 7572        active_parameter: Some(0),
 7573    };
 7574
 7575    // Ensure that signature_help is called when enabled afte edits
 7576    cx.update(|cx| {
 7577        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7578            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7579                settings.auto_signature_help = Some(false);
 7580                settings.show_signature_help_after_edits = Some(true);
 7581            });
 7582        });
 7583    });
 7584    cx.set_state(
 7585        &r#"
 7586            fn main() {
 7587                sampleˇ
 7588            }
 7589        "#
 7590        .unindent(),
 7591    );
 7592    cx.update_editor(|view, cx| {
 7593        view.handle_input("(", cx);
 7594    });
 7595    cx.assert_editor_state(
 7596        &"
 7597            fn main() {
 7598                sample(ˇ)
 7599            }
 7600        "
 7601        .unindent(),
 7602    );
 7603    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7604    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7605        .await;
 7606    cx.update_editor(|editor, _| {
 7607        let signature_help_state = editor.signature_help_state.popover().cloned();
 7608        assert!(signature_help_state.is_some());
 7609        let ParsedMarkdown {
 7610            text, highlights, ..
 7611        } = signature_help_state.unwrap().parsed_content;
 7612        assert_eq!(text, "param1: u8, param2: u8");
 7613        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7614        editor.signature_help_state = SignatureHelpState::default();
 7615    });
 7616
 7617    // Ensure that signature_help is called when auto signature help override is enabled
 7618    cx.update(|cx| {
 7619        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7620            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7621                settings.auto_signature_help = Some(true);
 7622                settings.show_signature_help_after_edits = Some(false);
 7623            });
 7624        });
 7625    });
 7626    cx.set_state(
 7627        &r#"
 7628            fn main() {
 7629                sampleˇ
 7630            }
 7631        "#
 7632        .unindent(),
 7633    );
 7634    cx.update_editor(|view, cx| {
 7635        view.handle_input("(", cx);
 7636    });
 7637    cx.assert_editor_state(
 7638        &"
 7639            fn main() {
 7640                sample(ˇ)
 7641            }
 7642        "
 7643        .unindent(),
 7644    );
 7645    handle_signature_help_request(&mut cx, mocked_response).await;
 7646    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7647        .await;
 7648    cx.editor(|editor, _| {
 7649        let signature_help_state = editor.signature_help_state.popover().cloned();
 7650        assert!(signature_help_state.is_some());
 7651        let ParsedMarkdown {
 7652            text, highlights, ..
 7653        } = signature_help_state.unwrap().parsed_content;
 7654        assert_eq!(text, "param1: u8, param2: u8");
 7655        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7656    });
 7657}
 7658
 7659#[gpui::test]
 7660async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7661    init_test(cx, |_| {});
 7662    cx.update(|cx| {
 7663        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7664            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7665                settings.auto_signature_help = Some(true);
 7666            });
 7667        });
 7668    });
 7669
 7670    let mut cx = EditorLspTestContext::new_rust(
 7671        lsp::ServerCapabilities {
 7672            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7673                ..Default::default()
 7674            }),
 7675            ..Default::default()
 7676        },
 7677        cx,
 7678    )
 7679    .await;
 7680
 7681    // A test that directly calls `show_signature_help`
 7682    cx.update_editor(|editor, cx| {
 7683        editor.show_signature_help(&ShowSignatureHelp, cx);
 7684    });
 7685
 7686    let mocked_response = lsp::SignatureHelp {
 7687        signatures: vec![lsp::SignatureInformation {
 7688            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7689            documentation: None,
 7690            parameters: Some(vec![
 7691                lsp::ParameterInformation {
 7692                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7693                    documentation: None,
 7694                },
 7695                lsp::ParameterInformation {
 7696                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7697                    documentation: None,
 7698                },
 7699            ]),
 7700            active_parameter: None,
 7701        }],
 7702        active_signature: Some(0),
 7703        active_parameter: Some(0),
 7704    };
 7705    handle_signature_help_request(&mut cx, mocked_response).await;
 7706
 7707    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7708        .await;
 7709
 7710    cx.editor(|editor, _| {
 7711        let signature_help_state = editor.signature_help_state.popover().cloned();
 7712        assert!(signature_help_state.is_some());
 7713        let ParsedMarkdown {
 7714            text, highlights, ..
 7715        } = signature_help_state.unwrap().parsed_content;
 7716        assert_eq!(text, "param1: u8, param2: u8");
 7717        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7718    });
 7719
 7720    // When exiting outside from inside the brackets, `signature_help` is closed.
 7721    cx.set_state(indoc! {"
 7722        fn main() {
 7723            sample(ˇ);
 7724        }
 7725
 7726        fn sample(param1: u8, param2: u8) {}
 7727    "});
 7728
 7729    cx.update_editor(|editor, cx| {
 7730        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7731    });
 7732
 7733    let mocked_response = lsp::SignatureHelp {
 7734        signatures: Vec::new(),
 7735        active_signature: None,
 7736        active_parameter: None,
 7737    };
 7738    handle_signature_help_request(&mut cx, mocked_response).await;
 7739
 7740    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7741        .await;
 7742
 7743    cx.editor(|editor, _| {
 7744        assert!(!editor.signature_help_state.is_shown());
 7745    });
 7746
 7747    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7748    cx.set_state(indoc! {"
 7749        fn main() {
 7750            sample(ˇ);
 7751        }
 7752
 7753        fn sample(param1: u8, param2: u8) {}
 7754    "});
 7755
 7756    let mocked_response = lsp::SignatureHelp {
 7757        signatures: vec![lsp::SignatureInformation {
 7758            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7759            documentation: None,
 7760            parameters: Some(vec![
 7761                lsp::ParameterInformation {
 7762                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7763                    documentation: None,
 7764                },
 7765                lsp::ParameterInformation {
 7766                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7767                    documentation: None,
 7768                },
 7769            ]),
 7770            active_parameter: None,
 7771        }],
 7772        active_signature: Some(0),
 7773        active_parameter: Some(0),
 7774    };
 7775    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7776    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7777        .await;
 7778    cx.editor(|editor, _| {
 7779        assert!(editor.signature_help_state.is_shown());
 7780    });
 7781
 7782    // Restore the popover with more parameter input
 7783    cx.set_state(indoc! {"
 7784        fn main() {
 7785            sample(param1, param2ˇ);
 7786        }
 7787
 7788        fn sample(param1: u8, param2: u8) {}
 7789    "});
 7790
 7791    let mocked_response = lsp::SignatureHelp {
 7792        signatures: vec![lsp::SignatureInformation {
 7793            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7794            documentation: None,
 7795            parameters: Some(vec![
 7796                lsp::ParameterInformation {
 7797                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7798                    documentation: None,
 7799                },
 7800                lsp::ParameterInformation {
 7801                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7802                    documentation: None,
 7803                },
 7804            ]),
 7805            active_parameter: None,
 7806        }],
 7807        active_signature: Some(0),
 7808        active_parameter: Some(1),
 7809    };
 7810    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7811    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7812        .await;
 7813
 7814    // When selecting a range, the popover is gone.
 7815    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7816    cx.update_editor(|editor, cx| {
 7817        editor.change_selections(None, cx, |s| {
 7818            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7819        })
 7820    });
 7821    cx.assert_editor_state(indoc! {"
 7822        fn main() {
 7823            sample(param1, «ˇparam2»);
 7824        }
 7825
 7826        fn sample(param1: u8, param2: u8) {}
 7827    "});
 7828    cx.editor(|editor, _| {
 7829        assert!(!editor.signature_help_state.is_shown());
 7830    });
 7831
 7832    // When unselecting again, the popover is back if within the brackets.
 7833    cx.update_editor(|editor, cx| {
 7834        editor.change_selections(None, cx, |s| {
 7835            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7836        })
 7837    });
 7838    cx.assert_editor_state(indoc! {"
 7839        fn main() {
 7840            sample(param1, ˇparam2);
 7841        }
 7842
 7843        fn sample(param1: u8, param2: u8) {}
 7844    "});
 7845    handle_signature_help_request(&mut cx, mocked_response).await;
 7846    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7847        .await;
 7848    cx.editor(|editor, _| {
 7849        assert!(editor.signature_help_state.is_shown());
 7850    });
 7851
 7852    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7853    cx.update_editor(|editor, cx| {
 7854        editor.change_selections(None, cx, |s| {
 7855            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7856            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7857        })
 7858    });
 7859    cx.assert_editor_state(indoc! {"
 7860        fn main() {
 7861            sample(param1, ˇparam2);
 7862        }
 7863
 7864        fn sample(param1: u8, param2: u8) {}
 7865    "});
 7866
 7867    let mocked_response = lsp::SignatureHelp {
 7868        signatures: vec![lsp::SignatureInformation {
 7869            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7870            documentation: None,
 7871            parameters: Some(vec![
 7872                lsp::ParameterInformation {
 7873                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7874                    documentation: None,
 7875                },
 7876                lsp::ParameterInformation {
 7877                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7878                    documentation: None,
 7879                },
 7880            ]),
 7881            active_parameter: None,
 7882        }],
 7883        active_signature: Some(0),
 7884        active_parameter: Some(1),
 7885    };
 7886    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7887    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7888        .await;
 7889    cx.update_editor(|editor, cx| {
 7890        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7891    });
 7892    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7893        .await;
 7894    cx.update_editor(|editor, cx| {
 7895        editor.change_selections(None, cx, |s| {
 7896            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7897        })
 7898    });
 7899    cx.assert_editor_state(indoc! {"
 7900        fn main() {
 7901            sample(param1, «ˇparam2»);
 7902        }
 7903
 7904        fn sample(param1: u8, param2: u8) {}
 7905    "});
 7906    cx.update_editor(|editor, cx| {
 7907        editor.change_selections(None, cx, |s| {
 7908            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7909        })
 7910    });
 7911    cx.assert_editor_state(indoc! {"
 7912        fn main() {
 7913            sample(param1, ˇparam2);
 7914        }
 7915
 7916        fn sample(param1: u8, param2: u8) {}
 7917    "});
 7918    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7919        .await;
 7920}
 7921
 7922#[gpui::test]
 7923async fn test_completion(cx: &mut gpui::TestAppContext) {
 7924    init_test(cx, |_| {});
 7925
 7926    let mut cx = EditorLspTestContext::new_rust(
 7927        lsp::ServerCapabilities {
 7928            completion_provider: Some(lsp::CompletionOptions {
 7929                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7930                resolve_provider: Some(true),
 7931                ..Default::default()
 7932            }),
 7933            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7934            ..Default::default()
 7935        },
 7936        cx,
 7937    )
 7938    .await;
 7939    let counter = Arc::new(AtomicUsize::new(0));
 7940
 7941    cx.set_state(indoc! {"
 7942        oneˇ
 7943        two
 7944        three
 7945    "});
 7946    cx.simulate_keystroke(".");
 7947    handle_completion_request(
 7948        &mut cx,
 7949        indoc! {"
 7950            one.|<>
 7951            two
 7952            three
 7953        "},
 7954        vec!["first_completion", "second_completion"],
 7955        counter.clone(),
 7956    )
 7957    .await;
 7958    cx.condition(|editor, _| editor.context_menu_visible())
 7959        .await;
 7960    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7961
 7962    let _handler = handle_signature_help_request(
 7963        &mut cx,
 7964        lsp::SignatureHelp {
 7965            signatures: vec![lsp::SignatureInformation {
 7966                label: "test signature".to_string(),
 7967                documentation: None,
 7968                parameters: Some(vec![lsp::ParameterInformation {
 7969                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7970                    documentation: None,
 7971                }]),
 7972                active_parameter: None,
 7973            }],
 7974            active_signature: None,
 7975            active_parameter: None,
 7976        },
 7977    );
 7978    cx.update_editor(|editor, cx| {
 7979        assert!(
 7980            !editor.signature_help_state.is_shown(),
 7981            "No signature help was called for"
 7982        );
 7983        editor.show_signature_help(&ShowSignatureHelp, cx);
 7984    });
 7985    cx.run_until_parked();
 7986    cx.update_editor(|editor, _| {
 7987        assert!(
 7988            !editor.signature_help_state.is_shown(),
 7989            "No signature help should be shown when completions menu is open"
 7990        );
 7991    });
 7992
 7993    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7994        editor.context_menu_next(&Default::default(), cx);
 7995        editor
 7996            .confirm_completion(&ConfirmCompletion::default(), cx)
 7997            .unwrap()
 7998    });
 7999    cx.assert_editor_state(indoc! {"
 8000        one.second_completionˇ
 8001        two
 8002        three
 8003    "});
 8004
 8005    handle_resolve_completion_request(
 8006        &mut cx,
 8007        Some(vec![
 8008            (
 8009                //This overlaps with the primary completion edit which is
 8010                //misbehavior from the LSP spec, test that we filter it out
 8011                indoc! {"
 8012                    one.second_ˇcompletion
 8013                    two
 8014                    threeˇ
 8015                "},
 8016                "overlapping additional edit",
 8017            ),
 8018            (
 8019                indoc! {"
 8020                    one.second_completion
 8021                    two
 8022                    threeˇ
 8023                "},
 8024                "\nadditional edit",
 8025            ),
 8026        ]),
 8027    )
 8028    .await;
 8029    apply_additional_edits.await.unwrap();
 8030    cx.assert_editor_state(indoc! {"
 8031        one.second_completionˇ
 8032        two
 8033        three
 8034        additional edit
 8035    "});
 8036
 8037    cx.set_state(indoc! {"
 8038        one.second_completion
 8039        twoˇ
 8040        threeˇ
 8041        additional edit
 8042    "});
 8043    cx.simulate_keystroke(" ");
 8044    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8045    cx.simulate_keystroke("s");
 8046    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8047
 8048    cx.assert_editor_state(indoc! {"
 8049        one.second_completion
 8050        two sˇ
 8051        three sˇ
 8052        additional edit
 8053    "});
 8054    handle_completion_request(
 8055        &mut cx,
 8056        indoc! {"
 8057            one.second_completion
 8058            two s
 8059            three <s|>
 8060            additional edit
 8061        "},
 8062        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8063        counter.clone(),
 8064    )
 8065    .await;
 8066    cx.condition(|editor, _| editor.context_menu_visible())
 8067        .await;
 8068    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8069
 8070    cx.simulate_keystroke("i");
 8071
 8072    handle_completion_request(
 8073        &mut cx,
 8074        indoc! {"
 8075            one.second_completion
 8076            two si
 8077            three <si|>
 8078            additional edit
 8079        "},
 8080        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8081        counter.clone(),
 8082    )
 8083    .await;
 8084    cx.condition(|editor, _| editor.context_menu_visible())
 8085        .await;
 8086    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8087
 8088    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8089        editor
 8090            .confirm_completion(&ConfirmCompletion::default(), cx)
 8091            .unwrap()
 8092    });
 8093    cx.assert_editor_state(indoc! {"
 8094        one.second_completion
 8095        two sixth_completionˇ
 8096        three sixth_completionˇ
 8097        additional edit
 8098    "});
 8099
 8100    handle_resolve_completion_request(&mut cx, None).await;
 8101    apply_additional_edits.await.unwrap();
 8102
 8103    cx.update(|cx| {
 8104        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8105            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8106                settings.show_completions_on_input = Some(false);
 8107            });
 8108        })
 8109    });
 8110    cx.set_state("editorˇ");
 8111    cx.simulate_keystroke(".");
 8112    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8113    cx.simulate_keystroke("c");
 8114    cx.simulate_keystroke("l");
 8115    cx.simulate_keystroke("o");
 8116    cx.assert_editor_state("editor.cloˇ");
 8117    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8118    cx.update_editor(|editor, cx| {
 8119        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8120    });
 8121    handle_completion_request(
 8122        &mut cx,
 8123        "editor.<clo|>",
 8124        vec!["close", "clobber"],
 8125        counter.clone(),
 8126    )
 8127    .await;
 8128    cx.condition(|editor, _| editor.context_menu_visible())
 8129        .await;
 8130    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8131
 8132    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8133        editor
 8134            .confirm_completion(&ConfirmCompletion::default(), cx)
 8135            .unwrap()
 8136    });
 8137    cx.assert_editor_state("editor.closeˇ");
 8138    handle_resolve_completion_request(&mut cx, None).await;
 8139    apply_additional_edits.await.unwrap();
 8140}
 8141
 8142#[gpui::test]
 8143async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8144    init_test(cx, |_| {});
 8145    let mut cx = EditorLspTestContext::new_rust(
 8146        lsp::ServerCapabilities {
 8147            completion_provider: Some(lsp::CompletionOptions {
 8148                trigger_characters: Some(vec![".".to_string()]),
 8149                ..Default::default()
 8150            }),
 8151            ..Default::default()
 8152        },
 8153        cx,
 8154    )
 8155    .await;
 8156    cx.lsp
 8157        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8158            Ok(Some(lsp::CompletionResponse::Array(vec![
 8159                lsp::CompletionItem {
 8160                    label: "first".into(),
 8161                    ..Default::default()
 8162                },
 8163                lsp::CompletionItem {
 8164                    label: "last".into(),
 8165                    ..Default::default()
 8166                },
 8167            ])))
 8168        });
 8169    cx.set_state("variableˇ");
 8170    cx.simulate_keystroke(".");
 8171    cx.executor().run_until_parked();
 8172
 8173    cx.update_editor(|editor, _| {
 8174        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8175            assert_eq!(
 8176                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8177                &["first", "last"]
 8178            );
 8179        } else {
 8180            panic!("expected completion menu to be open");
 8181        }
 8182    });
 8183
 8184    cx.update_editor(|editor, cx| {
 8185        editor.move_page_down(&MovePageDown::default(), cx);
 8186        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8187            assert!(
 8188                menu.selected_item == 1,
 8189                "expected PageDown to select the last item from the context menu"
 8190            );
 8191        } else {
 8192            panic!("expected completion menu to stay open after PageDown");
 8193        }
 8194    });
 8195
 8196    cx.update_editor(|editor, cx| {
 8197        editor.move_page_up(&MovePageUp::default(), cx);
 8198        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8199            assert!(
 8200                menu.selected_item == 0,
 8201                "expected PageUp to select the first item from the context menu"
 8202            );
 8203        } else {
 8204            panic!("expected completion menu to stay open after PageUp");
 8205        }
 8206    });
 8207}
 8208
 8209#[gpui::test]
 8210async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8211    init_test(cx, |_| {});
 8212
 8213    let mut cx = EditorLspTestContext::new_rust(
 8214        lsp::ServerCapabilities {
 8215            completion_provider: Some(lsp::CompletionOptions {
 8216                trigger_characters: Some(vec![".".to_string()]),
 8217                resolve_provider: Some(true),
 8218                ..Default::default()
 8219            }),
 8220            ..Default::default()
 8221        },
 8222        cx,
 8223    )
 8224    .await;
 8225
 8226    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8227    cx.simulate_keystroke(".");
 8228    let completion_item = lsp::CompletionItem {
 8229        label: "Some".into(),
 8230        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8231        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8232        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8233            kind: lsp::MarkupKind::Markdown,
 8234            value: "```rust\nSome(2)\n```".to_string(),
 8235        })),
 8236        deprecated: Some(false),
 8237        sort_text: Some("Some".to_string()),
 8238        filter_text: Some("Some".to_string()),
 8239        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8240        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8241            range: lsp::Range {
 8242                start: lsp::Position {
 8243                    line: 0,
 8244                    character: 22,
 8245                },
 8246                end: lsp::Position {
 8247                    line: 0,
 8248                    character: 22,
 8249                },
 8250            },
 8251            new_text: "Some(2)".to_string(),
 8252        })),
 8253        additional_text_edits: Some(vec![lsp::TextEdit {
 8254            range: lsp::Range {
 8255                start: lsp::Position {
 8256                    line: 0,
 8257                    character: 20,
 8258                },
 8259                end: lsp::Position {
 8260                    line: 0,
 8261                    character: 22,
 8262                },
 8263            },
 8264            new_text: "".to_string(),
 8265        }]),
 8266        ..Default::default()
 8267    };
 8268
 8269    let closure_completion_item = completion_item.clone();
 8270    let counter = Arc::new(AtomicUsize::new(0));
 8271    let counter_clone = counter.clone();
 8272    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8273        let task_completion_item = closure_completion_item.clone();
 8274        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8275        async move {
 8276            Ok(Some(lsp::CompletionResponse::Array(vec![
 8277                task_completion_item,
 8278            ])))
 8279        }
 8280    });
 8281
 8282    cx.condition(|editor, _| editor.context_menu_visible())
 8283        .await;
 8284    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8285    assert!(request.next().await.is_some());
 8286    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8287
 8288    cx.simulate_keystroke("S");
 8289    cx.simulate_keystroke("o");
 8290    cx.simulate_keystroke("m");
 8291    cx.condition(|editor, _| editor.context_menu_visible())
 8292        .await;
 8293    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8294    assert!(request.next().await.is_some());
 8295    assert!(request.next().await.is_some());
 8296    assert!(request.next().await.is_some());
 8297    request.close();
 8298    assert!(request.next().await.is_none());
 8299    assert_eq!(
 8300        counter.load(atomic::Ordering::Acquire),
 8301        4,
 8302        "With the completions menu open, only one LSP request should happen per input"
 8303    );
 8304}
 8305
 8306#[gpui::test]
 8307async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8308    init_test(cx, |_| {});
 8309    let mut cx = EditorTestContext::new(cx).await;
 8310    let language = Arc::new(Language::new(
 8311        LanguageConfig {
 8312            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8313            ..Default::default()
 8314        },
 8315        Some(tree_sitter_rust::LANGUAGE.into()),
 8316    ));
 8317    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8318
 8319    // If multiple selections intersect a line, the line is only toggled once.
 8320    cx.set_state(indoc! {"
 8321        fn a() {
 8322            «//b();
 8323            ˇ»// «c();
 8324            //ˇ»  d();
 8325        }
 8326    "});
 8327
 8328    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8329
 8330    cx.assert_editor_state(indoc! {"
 8331        fn a() {
 8332            «b();
 8333            c();
 8334            ˇ» d();
 8335        }
 8336    "});
 8337
 8338    // The comment prefix is inserted at the same column for every line in a
 8339    // selection.
 8340    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8341
 8342    cx.assert_editor_state(indoc! {"
 8343        fn a() {
 8344            // «b();
 8345            // c();
 8346            ˇ»//  d();
 8347        }
 8348    "});
 8349
 8350    // If a selection ends at the beginning of a line, that line is not toggled.
 8351    cx.set_selections_state(indoc! {"
 8352        fn a() {
 8353            // b();
 8354            «// c();
 8355        ˇ»    //  d();
 8356        }
 8357    "});
 8358
 8359    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8360
 8361    cx.assert_editor_state(indoc! {"
 8362        fn a() {
 8363            // b();
 8364            «c();
 8365        ˇ»    //  d();
 8366        }
 8367    "});
 8368
 8369    // If a selection span a single line and is empty, the line is toggled.
 8370    cx.set_state(indoc! {"
 8371        fn a() {
 8372            a();
 8373            b();
 8374        ˇ
 8375        }
 8376    "});
 8377
 8378    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8379
 8380    cx.assert_editor_state(indoc! {"
 8381        fn a() {
 8382            a();
 8383            b();
 8384        //•ˇ
 8385        }
 8386    "});
 8387
 8388    // If a selection span multiple lines, empty lines are not toggled.
 8389    cx.set_state(indoc! {"
 8390        fn a() {
 8391            «a();
 8392
 8393            c();ˇ»
 8394        }
 8395    "});
 8396
 8397    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8398
 8399    cx.assert_editor_state(indoc! {"
 8400        fn a() {
 8401            // «a();
 8402
 8403            // c();ˇ»
 8404        }
 8405    "});
 8406
 8407    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8408    cx.set_state(indoc! {"
 8409        fn a() {
 8410            «// a();
 8411            /// b();
 8412            //! c();ˇ»
 8413        }
 8414    "});
 8415
 8416    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8417
 8418    cx.assert_editor_state(indoc! {"
 8419        fn a() {
 8420            «a();
 8421            b();
 8422            c();ˇ»
 8423        }
 8424    "});
 8425}
 8426
 8427#[gpui::test]
 8428async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8429    init_test(cx, |_| {});
 8430
 8431    let language = Arc::new(Language::new(
 8432        LanguageConfig {
 8433            line_comments: vec!["// ".into()],
 8434            ..Default::default()
 8435        },
 8436        Some(tree_sitter_rust::LANGUAGE.into()),
 8437    ));
 8438
 8439    let mut cx = EditorTestContext::new(cx).await;
 8440
 8441    cx.language_registry().add(language.clone());
 8442    cx.update_buffer(|buffer, cx| {
 8443        buffer.set_language(Some(language), cx);
 8444    });
 8445
 8446    let toggle_comments = &ToggleComments {
 8447        advance_downwards: true,
 8448    };
 8449
 8450    // Single cursor on one line -> advance
 8451    // Cursor moves horizontally 3 characters as well on non-blank line
 8452    cx.set_state(indoc!(
 8453        "fn a() {
 8454             ˇdog();
 8455             cat();
 8456        }"
 8457    ));
 8458    cx.update_editor(|editor, cx| {
 8459        editor.toggle_comments(toggle_comments, cx);
 8460    });
 8461    cx.assert_editor_state(indoc!(
 8462        "fn a() {
 8463             // dog();
 8464             catˇ();
 8465        }"
 8466    ));
 8467
 8468    // Single selection on one line -> don't advance
 8469    cx.set_state(indoc!(
 8470        "fn a() {
 8471             «dog()ˇ»;
 8472             cat();
 8473        }"
 8474    ));
 8475    cx.update_editor(|editor, cx| {
 8476        editor.toggle_comments(toggle_comments, cx);
 8477    });
 8478    cx.assert_editor_state(indoc!(
 8479        "fn a() {
 8480             // «dog()ˇ»;
 8481             cat();
 8482        }"
 8483    ));
 8484
 8485    // Multiple cursors on one line -> advance
 8486    cx.set_state(indoc!(
 8487        "fn a() {
 8488             ˇdˇog();
 8489             cat();
 8490        }"
 8491    ));
 8492    cx.update_editor(|editor, cx| {
 8493        editor.toggle_comments(toggle_comments, cx);
 8494    });
 8495    cx.assert_editor_state(indoc!(
 8496        "fn a() {
 8497             // dog();
 8498             catˇ(ˇ);
 8499        }"
 8500    ));
 8501
 8502    // Multiple cursors on one line, with selection -> don't advance
 8503    cx.set_state(indoc!(
 8504        "fn a() {
 8505             ˇdˇog«()ˇ»;
 8506             cat();
 8507        }"
 8508    ));
 8509    cx.update_editor(|editor, cx| {
 8510        editor.toggle_comments(toggle_comments, cx);
 8511    });
 8512    cx.assert_editor_state(indoc!(
 8513        "fn a() {
 8514             // ˇdˇog«()ˇ»;
 8515             cat();
 8516        }"
 8517    ));
 8518
 8519    // Single cursor on one line -> advance
 8520    // Cursor moves to column 0 on blank line
 8521    cx.set_state(indoc!(
 8522        "fn a() {
 8523             ˇdog();
 8524
 8525             cat();
 8526        }"
 8527    ));
 8528    cx.update_editor(|editor, cx| {
 8529        editor.toggle_comments(toggle_comments, cx);
 8530    });
 8531    cx.assert_editor_state(indoc!(
 8532        "fn a() {
 8533             // dog();
 8534        ˇ
 8535             cat();
 8536        }"
 8537    ));
 8538
 8539    // Single cursor on one line -> advance
 8540    // Cursor starts and ends at column 0
 8541    cx.set_state(indoc!(
 8542        "fn a() {
 8543         ˇ    dog();
 8544             cat();
 8545        }"
 8546    ));
 8547    cx.update_editor(|editor, cx| {
 8548        editor.toggle_comments(toggle_comments, cx);
 8549    });
 8550    cx.assert_editor_state(indoc!(
 8551        "fn a() {
 8552             // dog();
 8553         ˇ    cat();
 8554        }"
 8555    ));
 8556}
 8557
 8558#[gpui::test]
 8559async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8560    init_test(cx, |_| {});
 8561
 8562    let mut cx = EditorTestContext::new(cx).await;
 8563
 8564    let html_language = Arc::new(
 8565        Language::new(
 8566            LanguageConfig {
 8567                name: "HTML".into(),
 8568                block_comment: Some(("<!-- ".into(), " -->".into())),
 8569                ..Default::default()
 8570            },
 8571            Some(tree_sitter_html::language()),
 8572        )
 8573        .with_injection_query(
 8574            r#"
 8575            (script_element
 8576                (raw_text) @content
 8577                (#set! "language" "javascript"))
 8578            "#,
 8579        )
 8580        .unwrap(),
 8581    );
 8582
 8583    let javascript_language = Arc::new(Language::new(
 8584        LanguageConfig {
 8585            name: "JavaScript".into(),
 8586            line_comments: vec!["// ".into()],
 8587            ..Default::default()
 8588        },
 8589        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8590    ));
 8591
 8592    cx.language_registry().add(html_language.clone());
 8593    cx.language_registry().add(javascript_language.clone());
 8594    cx.update_buffer(|buffer, cx| {
 8595        buffer.set_language(Some(html_language), cx);
 8596    });
 8597
 8598    // Toggle comments for empty selections
 8599    cx.set_state(
 8600        &r#"
 8601            <p>A</p>ˇ
 8602            <p>B</p>ˇ
 8603            <p>C</p>ˇ
 8604        "#
 8605        .unindent(),
 8606    );
 8607    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8608    cx.assert_editor_state(
 8609        &r#"
 8610            <!-- <p>A</p>ˇ -->
 8611            <!-- <p>B</p>ˇ -->
 8612            <!-- <p>C</p>ˇ -->
 8613        "#
 8614        .unindent(),
 8615    );
 8616    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8617    cx.assert_editor_state(
 8618        &r#"
 8619            <p>A</p>ˇ
 8620            <p>B</p>ˇ
 8621            <p>C</p>ˇ
 8622        "#
 8623        .unindent(),
 8624    );
 8625
 8626    // Toggle comments for mixture of empty and non-empty selections, where
 8627    // multiple selections occupy a given line.
 8628    cx.set_state(
 8629        &r#"
 8630            <p>A«</p>
 8631            <p>ˇ»B</p>ˇ
 8632            <p>C«</p>
 8633            <p>ˇ»D</p>ˇ
 8634        "#
 8635        .unindent(),
 8636    );
 8637
 8638    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8639    cx.assert_editor_state(
 8640        &r#"
 8641            <!-- <p>A«</p>
 8642            <p>ˇ»B</p>ˇ -->
 8643            <!-- <p>C«</p>
 8644            <p>ˇ»D</p>ˇ -->
 8645        "#
 8646        .unindent(),
 8647    );
 8648    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8649    cx.assert_editor_state(
 8650        &r#"
 8651            <p>A«</p>
 8652            <p>ˇ»B</p>ˇ
 8653            <p>C«</p>
 8654            <p>ˇ»D</p>ˇ
 8655        "#
 8656        .unindent(),
 8657    );
 8658
 8659    // Toggle comments when different languages are active for different
 8660    // selections.
 8661    cx.set_state(
 8662        &r#"
 8663            ˇ<script>
 8664                ˇvar x = new Y();
 8665            ˇ</script>
 8666        "#
 8667        .unindent(),
 8668    );
 8669    cx.executor().run_until_parked();
 8670    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8671    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8672    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8673    cx.assert_editor_state(
 8674        &r#"
 8675            <!-- ˇ<script> -->
 8676                // ˇvar x = new Y();
 8677            // ˇ</script>
 8678        "#
 8679        .unindent(),
 8680    );
 8681}
 8682
 8683#[gpui::test]
 8684fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8685    init_test(cx, |_| {});
 8686
 8687    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8688    let multibuffer = cx.new_model(|cx| {
 8689        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8690        multibuffer.push_excerpts(
 8691            buffer.clone(),
 8692            [
 8693                ExcerptRange {
 8694                    context: Point::new(0, 0)..Point::new(0, 4),
 8695                    primary: None,
 8696                },
 8697                ExcerptRange {
 8698                    context: Point::new(1, 0)..Point::new(1, 4),
 8699                    primary: None,
 8700                },
 8701            ],
 8702            cx,
 8703        );
 8704        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8705        multibuffer
 8706    });
 8707
 8708    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8709    view.update(cx, |view, cx| {
 8710        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8711        view.change_selections(None, cx, |s| {
 8712            s.select_ranges([
 8713                Point::new(0, 0)..Point::new(0, 0),
 8714                Point::new(1, 0)..Point::new(1, 0),
 8715            ])
 8716        });
 8717
 8718        view.handle_input("X", cx);
 8719        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8720        assert_eq!(
 8721            view.selections.ranges(cx),
 8722            [
 8723                Point::new(0, 1)..Point::new(0, 1),
 8724                Point::new(1, 1)..Point::new(1, 1),
 8725            ]
 8726        );
 8727
 8728        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8729        view.change_selections(None, cx, |s| {
 8730            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8731        });
 8732        view.backspace(&Default::default(), cx);
 8733        assert_eq!(view.text(cx), "Xa\nbbb");
 8734        assert_eq!(
 8735            view.selections.ranges(cx),
 8736            [Point::new(1, 0)..Point::new(1, 0)]
 8737        );
 8738
 8739        view.change_selections(None, cx, |s| {
 8740            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8741        });
 8742        view.backspace(&Default::default(), cx);
 8743        assert_eq!(view.text(cx), "X\nbb");
 8744        assert_eq!(
 8745            view.selections.ranges(cx),
 8746            [Point::new(0, 1)..Point::new(0, 1)]
 8747        );
 8748    });
 8749}
 8750
 8751#[gpui::test]
 8752fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8753    init_test(cx, |_| {});
 8754
 8755    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8756    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8757        indoc! {"
 8758            [aaaa
 8759            (bbbb]
 8760            cccc)",
 8761        },
 8762        markers.clone(),
 8763    );
 8764    let excerpt_ranges = markers.into_iter().map(|marker| {
 8765        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8766        ExcerptRange {
 8767            context,
 8768            primary: None,
 8769        }
 8770    });
 8771    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8772    let multibuffer = cx.new_model(|cx| {
 8773        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8774        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8775        multibuffer
 8776    });
 8777
 8778    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8779    view.update(cx, |view, cx| {
 8780        let (expected_text, selection_ranges) = marked_text_ranges(
 8781            indoc! {"
 8782                aaaa
 8783                bˇbbb
 8784                bˇbbˇb
 8785                cccc"
 8786            },
 8787            true,
 8788        );
 8789        assert_eq!(view.text(cx), expected_text);
 8790        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8791
 8792        view.handle_input("X", cx);
 8793
 8794        let (expected_text, expected_selections) = marked_text_ranges(
 8795            indoc! {"
 8796                aaaa
 8797                bXˇbbXb
 8798                bXˇbbXˇb
 8799                cccc"
 8800            },
 8801            false,
 8802        );
 8803        assert_eq!(view.text(cx), expected_text);
 8804        assert_eq!(view.selections.ranges(cx), expected_selections);
 8805
 8806        view.newline(&Newline, cx);
 8807        let (expected_text, expected_selections) = marked_text_ranges(
 8808            indoc! {"
 8809                aaaa
 8810                bX
 8811                ˇbbX
 8812                b
 8813                bX
 8814                ˇbbX
 8815                ˇb
 8816                cccc"
 8817            },
 8818            false,
 8819        );
 8820        assert_eq!(view.text(cx), expected_text);
 8821        assert_eq!(view.selections.ranges(cx), expected_selections);
 8822    });
 8823}
 8824
 8825#[gpui::test]
 8826fn test_refresh_selections(cx: &mut TestAppContext) {
 8827    init_test(cx, |_| {});
 8828
 8829    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8830    let mut excerpt1_id = None;
 8831    let multibuffer = cx.new_model(|cx| {
 8832        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8833        excerpt1_id = multibuffer
 8834            .push_excerpts(
 8835                buffer.clone(),
 8836                [
 8837                    ExcerptRange {
 8838                        context: Point::new(0, 0)..Point::new(1, 4),
 8839                        primary: None,
 8840                    },
 8841                    ExcerptRange {
 8842                        context: Point::new(1, 0)..Point::new(2, 4),
 8843                        primary: None,
 8844                    },
 8845                ],
 8846                cx,
 8847            )
 8848            .into_iter()
 8849            .next();
 8850        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8851        multibuffer
 8852    });
 8853
 8854    let editor = cx.add_window(|cx| {
 8855        let mut editor = build_editor(multibuffer.clone(), cx);
 8856        let snapshot = editor.snapshot(cx);
 8857        editor.change_selections(None, cx, |s| {
 8858            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8859        });
 8860        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8861        assert_eq!(
 8862            editor.selections.ranges(cx),
 8863            [
 8864                Point::new(1, 3)..Point::new(1, 3),
 8865                Point::new(2, 1)..Point::new(2, 1),
 8866            ]
 8867        );
 8868        editor
 8869    });
 8870
 8871    // Refreshing selections is a no-op when excerpts haven't changed.
 8872    _ = editor.update(cx, |editor, cx| {
 8873        editor.change_selections(None, cx, |s| s.refresh());
 8874        assert_eq!(
 8875            editor.selections.ranges(cx),
 8876            [
 8877                Point::new(1, 3)..Point::new(1, 3),
 8878                Point::new(2, 1)..Point::new(2, 1),
 8879            ]
 8880        );
 8881    });
 8882
 8883    multibuffer.update(cx, |multibuffer, cx| {
 8884        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8885    });
 8886    _ = editor.update(cx, |editor, cx| {
 8887        // Removing an excerpt causes the first selection to become degenerate.
 8888        assert_eq!(
 8889            editor.selections.ranges(cx),
 8890            [
 8891                Point::new(0, 0)..Point::new(0, 0),
 8892                Point::new(0, 1)..Point::new(0, 1)
 8893            ]
 8894        );
 8895
 8896        // Refreshing selections will relocate the first selection to the original buffer
 8897        // location.
 8898        editor.change_selections(None, cx, |s| s.refresh());
 8899        assert_eq!(
 8900            editor.selections.ranges(cx),
 8901            [
 8902                Point::new(0, 1)..Point::new(0, 1),
 8903                Point::new(0, 3)..Point::new(0, 3)
 8904            ]
 8905        );
 8906        assert!(editor.selections.pending_anchor().is_some());
 8907    });
 8908}
 8909
 8910#[gpui::test]
 8911fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8912    init_test(cx, |_| {});
 8913
 8914    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8915    let mut excerpt1_id = None;
 8916    let multibuffer = cx.new_model(|cx| {
 8917        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8918        excerpt1_id = multibuffer
 8919            .push_excerpts(
 8920                buffer.clone(),
 8921                [
 8922                    ExcerptRange {
 8923                        context: Point::new(0, 0)..Point::new(1, 4),
 8924                        primary: None,
 8925                    },
 8926                    ExcerptRange {
 8927                        context: Point::new(1, 0)..Point::new(2, 4),
 8928                        primary: None,
 8929                    },
 8930                ],
 8931                cx,
 8932            )
 8933            .into_iter()
 8934            .next();
 8935        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8936        multibuffer
 8937    });
 8938
 8939    let editor = cx.add_window(|cx| {
 8940        let mut editor = build_editor(multibuffer.clone(), cx);
 8941        let snapshot = editor.snapshot(cx);
 8942        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8943        assert_eq!(
 8944            editor.selections.ranges(cx),
 8945            [Point::new(1, 3)..Point::new(1, 3)]
 8946        );
 8947        editor
 8948    });
 8949
 8950    multibuffer.update(cx, |multibuffer, cx| {
 8951        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8952    });
 8953    _ = editor.update(cx, |editor, cx| {
 8954        assert_eq!(
 8955            editor.selections.ranges(cx),
 8956            [Point::new(0, 0)..Point::new(0, 0)]
 8957        );
 8958
 8959        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8960        editor.change_selections(None, cx, |s| s.refresh());
 8961        assert_eq!(
 8962            editor.selections.ranges(cx),
 8963            [Point::new(0, 3)..Point::new(0, 3)]
 8964        );
 8965        assert!(editor.selections.pending_anchor().is_some());
 8966    });
 8967}
 8968
 8969#[gpui::test]
 8970async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8971    init_test(cx, |_| {});
 8972
 8973    let language = Arc::new(
 8974        Language::new(
 8975            LanguageConfig {
 8976                brackets: BracketPairConfig {
 8977                    pairs: vec![
 8978                        BracketPair {
 8979                            start: "{".to_string(),
 8980                            end: "}".to_string(),
 8981                            close: true,
 8982                            surround: true,
 8983                            newline: true,
 8984                        },
 8985                        BracketPair {
 8986                            start: "/* ".to_string(),
 8987                            end: " */".to_string(),
 8988                            close: true,
 8989                            surround: true,
 8990                            newline: true,
 8991                        },
 8992                    ],
 8993                    ..Default::default()
 8994                },
 8995                ..Default::default()
 8996            },
 8997            Some(tree_sitter_rust::LANGUAGE.into()),
 8998        )
 8999        .with_indents_query("")
 9000        .unwrap(),
 9001    );
 9002
 9003    let text = concat!(
 9004        "{   }\n",     //
 9005        "  x\n",       //
 9006        "  /*   */\n", //
 9007        "x\n",         //
 9008        "{{} }\n",     //
 9009    );
 9010
 9011    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9012    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9013    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9014    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9015        .await;
 9016
 9017    view.update(cx, |view, cx| {
 9018        view.change_selections(None, cx, |s| {
 9019            s.select_display_ranges([
 9020                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9021                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9022                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9023            ])
 9024        });
 9025        view.newline(&Newline, cx);
 9026
 9027        assert_eq!(
 9028            view.buffer().read(cx).read(cx).text(),
 9029            concat!(
 9030                "{ \n",    // Suppress rustfmt
 9031                "\n",      //
 9032                "}\n",     //
 9033                "  x\n",   //
 9034                "  /* \n", //
 9035                "  \n",    //
 9036                "  */\n",  //
 9037                "x\n",     //
 9038                "{{} \n",  //
 9039                "}\n",     //
 9040            )
 9041        );
 9042    });
 9043}
 9044
 9045#[gpui::test]
 9046fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9047    init_test(cx, |_| {});
 9048
 9049    let editor = cx.add_window(|cx| {
 9050        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9051        build_editor(buffer.clone(), cx)
 9052    });
 9053
 9054    _ = editor.update(cx, |editor, cx| {
 9055        struct Type1;
 9056        struct Type2;
 9057
 9058        let buffer = editor.buffer.read(cx).snapshot(cx);
 9059
 9060        let anchor_range =
 9061            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9062
 9063        editor.highlight_background::<Type1>(
 9064            &[
 9065                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9066                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9067                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9068                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9069            ],
 9070            |_| Hsla::red(),
 9071            cx,
 9072        );
 9073        editor.highlight_background::<Type2>(
 9074            &[
 9075                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9076                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9077                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9078                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9079            ],
 9080            |_| Hsla::green(),
 9081            cx,
 9082        );
 9083
 9084        let snapshot = editor.snapshot(cx);
 9085        let mut highlighted_ranges = editor.background_highlights_in_range(
 9086            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9087            &snapshot,
 9088            cx.theme().colors(),
 9089        );
 9090        // Enforce a consistent ordering based on color without relying on the ordering of the
 9091        // highlight's `TypeId` which is non-executor.
 9092        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9093        assert_eq!(
 9094            highlighted_ranges,
 9095            &[
 9096                (
 9097                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9098                    Hsla::red(),
 9099                ),
 9100                (
 9101                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9102                    Hsla::red(),
 9103                ),
 9104                (
 9105                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9106                    Hsla::green(),
 9107                ),
 9108                (
 9109                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9110                    Hsla::green(),
 9111                ),
 9112            ]
 9113        );
 9114        assert_eq!(
 9115            editor.background_highlights_in_range(
 9116                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9117                &snapshot,
 9118                cx.theme().colors(),
 9119            ),
 9120            &[(
 9121                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9122                Hsla::red(),
 9123            )]
 9124        );
 9125    });
 9126}
 9127
 9128#[gpui::test]
 9129async fn test_following(cx: &mut gpui::TestAppContext) {
 9130    init_test(cx, |_| {});
 9131
 9132    let fs = FakeFs::new(cx.executor());
 9133    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9134
 9135    let buffer = project.update(cx, |project, cx| {
 9136        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9137        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9138    });
 9139    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9140    let follower = cx.update(|cx| {
 9141        cx.open_window(
 9142            WindowOptions {
 9143                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9144                    gpui::Point::new(px(0.), px(0.)),
 9145                    gpui::Point::new(px(10.), px(80.)),
 9146                ))),
 9147                ..Default::default()
 9148            },
 9149            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9150        )
 9151        .unwrap()
 9152    });
 9153
 9154    let is_still_following = Rc::new(RefCell::new(true));
 9155    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9156    let pending_update = Rc::new(RefCell::new(None));
 9157    _ = follower.update(cx, {
 9158        let update = pending_update.clone();
 9159        let is_still_following = is_still_following.clone();
 9160        let follower_edit_event_count = follower_edit_event_count.clone();
 9161        |_, cx| {
 9162            cx.subscribe(
 9163                &leader.root_view(cx).unwrap(),
 9164                move |_, leader, event, cx| {
 9165                    leader
 9166                        .read(cx)
 9167                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9168                },
 9169            )
 9170            .detach();
 9171
 9172            cx.subscribe(
 9173                &follower.root_view(cx).unwrap(),
 9174                move |_, _, event: &EditorEvent, _cx| {
 9175                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9176                        *is_still_following.borrow_mut() = false;
 9177                    }
 9178
 9179                    if let EditorEvent::BufferEdited = event {
 9180                        *follower_edit_event_count.borrow_mut() += 1;
 9181                    }
 9182                },
 9183            )
 9184            .detach();
 9185        }
 9186    });
 9187
 9188    // Update the selections only
 9189    _ = leader.update(cx, |leader, cx| {
 9190        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9191    });
 9192    follower
 9193        .update(cx, |follower, cx| {
 9194            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9195        })
 9196        .unwrap()
 9197        .await
 9198        .unwrap();
 9199    _ = follower.update(cx, |follower, cx| {
 9200        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9201    });
 9202    assert!(*is_still_following.borrow());
 9203    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9204
 9205    // Update the scroll position only
 9206    _ = leader.update(cx, |leader, cx| {
 9207        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9208    });
 9209    follower
 9210        .update(cx, |follower, cx| {
 9211            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9212        })
 9213        .unwrap()
 9214        .await
 9215        .unwrap();
 9216    assert_eq!(
 9217        follower
 9218            .update(cx, |follower, cx| follower.scroll_position(cx))
 9219            .unwrap(),
 9220        gpui::Point::new(1.5, 3.5)
 9221    );
 9222    assert!(*is_still_following.borrow());
 9223    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9224
 9225    // Update the selections and scroll position. The follower's scroll position is updated
 9226    // via autoscroll, not via the leader's exact scroll position.
 9227    _ = leader.update(cx, |leader, cx| {
 9228        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9229        leader.request_autoscroll(Autoscroll::newest(), cx);
 9230        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9231    });
 9232    follower
 9233        .update(cx, |follower, cx| {
 9234            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9235        })
 9236        .unwrap()
 9237        .await
 9238        .unwrap();
 9239    _ = follower.update(cx, |follower, cx| {
 9240        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9241        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9242    });
 9243    assert!(*is_still_following.borrow());
 9244
 9245    // Creating a pending selection that precedes another selection
 9246    _ = leader.update(cx, |leader, cx| {
 9247        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9248        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9249    });
 9250    follower
 9251        .update(cx, |follower, cx| {
 9252            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9253        })
 9254        .unwrap()
 9255        .await
 9256        .unwrap();
 9257    _ = follower.update(cx, |follower, cx| {
 9258        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9259    });
 9260    assert!(*is_still_following.borrow());
 9261
 9262    // Extend the pending selection so that it surrounds another selection
 9263    _ = leader.update(cx, |leader, cx| {
 9264        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9265    });
 9266    follower
 9267        .update(cx, |follower, cx| {
 9268            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9269        })
 9270        .unwrap()
 9271        .await
 9272        .unwrap();
 9273    _ = follower.update(cx, |follower, cx| {
 9274        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9275    });
 9276
 9277    // Scrolling locally breaks the follow
 9278    _ = follower.update(cx, |follower, cx| {
 9279        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9280        follower.set_scroll_anchor(
 9281            ScrollAnchor {
 9282                anchor: top_anchor,
 9283                offset: gpui::Point::new(0.0, 0.5),
 9284            },
 9285            cx,
 9286        );
 9287    });
 9288    assert!(!(*is_still_following.borrow()));
 9289}
 9290
 9291#[gpui::test]
 9292async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9293    init_test(cx, |_| {});
 9294
 9295    let fs = FakeFs::new(cx.executor());
 9296    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9297    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9298    let pane = workspace
 9299        .update(cx, |workspace, _| workspace.active_pane().clone())
 9300        .unwrap();
 9301
 9302    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9303
 9304    let leader = pane.update(cx, |_, cx| {
 9305        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9306        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9307    });
 9308
 9309    // Start following the editor when it has no excerpts.
 9310    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9311    let follower_1 = cx
 9312        .update_window(*workspace.deref(), |_, cx| {
 9313            Editor::from_state_proto(
 9314                workspace.root_view(cx).unwrap(),
 9315                ViewId {
 9316                    creator: Default::default(),
 9317                    id: 0,
 9318                },
 9319                &mut state_message,
 9320                cx,
 9321            )
 9322        })
 9323        .unwrap()
 9324        .unwrap()
 9325        .await
 9326        .unwrap();
 9327
 9328    let update_message = Rc::new(RefCell::new(None));
 9329    follower_1.update(cx, {
 9330        let update = update_message.clone();
 9331        |_, cx| {
 9332            cx.subscribe(&leader, move |_, leader, event, cx| {
 9333                leader
 9334                    .read(cx)
 9335                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9336            })
 9337            .detach();
 9338        }
 9339    });
 9340
 9341    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9342        (
 9343            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9344            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9345        )
 9346    });
 9347
 9348    // Insert some excerpts.
 9349    leader.update(cx, |leader, cx| {
 9350        leader.buffer.update(cx, |multibuffer, cx| {
 9351            let excerpt_ids = multibuffer.push_excerpts(
 9352                buffer_1.clone(),
 9353                [
 9354                    ExcerptRange {
 9355                        context: 1..6,
 9356                        primary: None,
 9357                    },
 9358                    ExcerptRange {
 9359                        context: 12..15,
 9360                        primary: None,
 9361                    },
 9362                    ExcerptRange {
 9363                        context: 0..3,
 9364                        primary: None,
 9365                    },
 9366                ],
 9367                cx,
 9368            );
 9369            multibuffer.insert_excerpts_after(
 9370                excerpt_ids[0],
 9371                buffer_2.clone(),
 9372                [
 9373                    ExcerptRange {
 9374                        context: 8..12,
 9375                        primary: None,
 9376                    },
 9377                    ExcerptRange {
 9378                        context: 0..6,
 9379                        primary: None,
 9380                    },
 9381                ],
 9382                cx,
 9383            );
 9384        });
 9385    });
 9386
 9387    // Apply the update of adding the excerpts.
 9388    follower_1
 9389        .update(cx, |follower, cx| {
 9390            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9391        })
 9392        .await
 9393        .unwrap();
 9394    assert_eq!(
 9395        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9396        leader.update(cx, |editor, cx| editor.text(cx))
 9397    );
 9398    update_message.borrow_mut().take();
 9399
 9400    // Start following separately after it already has excerpts.
 9401    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9402    let follower_2 = cx
 9403        .update_window(*workspace.deref(), |_, cx| {
 9404            Editor::from_state_proto(
 9405                workspace.root_view(cx).unwrap().clone(),
 9406                ViewId {
 9407                    creator: Default::default(),
 9408                    id: 0,
 9409                },
 9410                &mut state_message,
 9411                cx,
 9412            )
 9413        })
 9414        .unwrap()
 9415        .unwrap()
 9416        .await
 9417        .unwrap();
 9418    assert_eq!(
 9419        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9420        leader.update(cx, |editor, cx| editor.text(cx))
 9421    );
 9422
 9423    // Remove some excerpts.
 9424    leader.update(cx, |leader, cx| {
 9425        leader.buffer.update(cx, |multibuffer, cx| {
 9426            let excerpt_ids = multibuffer.excerpt_ids();
 9427            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9428            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9429        });
 9430    });
 9431
 9432    // Apply the update of removing the excerpts.
 9433    follower_1
 9434        .update(cx, |follower, cx| {
 9435            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9436        })
 9437        .await
 9438        .unwrap();
 9439    follower_2
 9440        .update(cx, |follower, cx| {
 9441            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9442        })
 9443        .await
 9444        .unwrap();
 9445    update_message.borrow_mut().take();
 9446    assert_eq!(
 9447        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9448        leader.update(cx, |editor, cx| editor.text(cx))
 9449    );
 9450}
 9451
 9452#[gpui::test]
 9453async fn go_to_prev_overlapping_diagnostic(
 9454    executor: BackgroundExecutor,
 9455    cx: &mut gpui::TestAppContext,
 9456) {
 9457    init_test(cx, |_| {});
 9458
 9459    let mut cx = EditorTestContext::new(cx).await;
 9460    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9461
 9462    cx.set_state(indoc! {"
 9463        ˇfn func(abc def: i32) -> u32 {
 9464        }
 9465    "});
 9466
 9467    cx.update(|cx| {
 9468        project.update(cx, |project, cx| {
 9469            project
 9470                .update_diagnostics(
 9471                    LanguageServerId(0),
 9472                    lsp::PublishDiagnosticsParams {
 9473                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9474                        version: None,
 9475                        diagnostics: vec![
 9476                            lsp::Diagnostic {
 9477                                range: lsp::Range::new(
 9478                                    lsp::Position::new(0, 11),
 9479                                    lsp::Position::new(0, 12),
 9480                                ),
 9481                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9482                                ..Default::default()
 9483                            },
 9484                            lsp::Diagnostic {
 9485                                range: lsp::Range::new(
 9486                                    lsp::Position::new(0, 12),
 9487                                    lsp::Position::new(0, 15),
 9488                                ),
 9489                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9490                                ..Default::default()
 9491                            },
 9492                            lsp::Diagnostic {
 9493                                range: lsp::Range::new(
 9494                                    lsp::Position::new(0, 25),
 9495                                    lsp::Position::new(0, 28),
 9496                                ),
 9497                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9498                                ..Default::default()
 9499                            },
 9500                        ],
 9501                    },
 9502                    &[],
 9503                    cx,
 9504                )
 9505                .unwrap()
 9506        });
 9507    });
 9508
 9509    executor.run_until_parked();
 9510
 9511    cx.update_editor(|editor, cx| {
 9512        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9513    });
 9514
 9515    cx.assert_editor_state(indoc! {"
 9516        fn func(abc def: i32) -> ˇu32 {
 9517        }
 9518    "});
 9519
 9520    cx.update_editor(|editor, cx| {
 9521        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9522    });
 9523
 9524    cx.assert_editor_state(indoc! {"
 9525        fn func(abc ˇdef: i32) -> u32 {
 9526        }
 9527    "});
 9528
 9529    cx.update_editor(|editor, cx| {
 9530        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9531    });
 9532
 9533    cx.assert_editor_state(indoc! {"
 9534        fn func(abcˇ def: i32) -> u32 {
 9535        }
 9536    "});
 9537
 9538    cx.update_editor(|editor, cx| {
 9539        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9540    });
 9541
 9542    cx.assert_editor_state(indoc! {"
 9543        fn func(abc def: i32) -> ˇu32 {
 9544        }
 9545    "});
 9546}
 9547
 9548#[gpui::test]
 9549async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9550    init_test(cx, |_| {});
 9551
 9552    let mut cx = EditorTestContext::new(cx).await;
 9553
 9554    cx.set_state(indoc! {"
 9555        fn func(abˇc def: i32) -> u32 {
 9556        }
 9557    "});
 9558    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9559
 9560    cx.update(|cx| {
 9561        project.update(cx, |project, cx| {
 9562            project.update_diagnostics(
 9563                LanguageServerId(0),
 9564                lsp::PublishDiagnosticsParams {
 9565                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9566                    version: None,
 9567                    diagnostics: vec![lsp::Diagnostic {
 9568                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9569                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9570                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9571                        ..Default::default()
 9572                    }],
 9573                },
 9574                &[],
 9575                cx,
 9576            )
 9577        })
 9578    }).unwrap();
 9579    cx.run_until_parked();
 9580    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9581    cx.run_until_parked();
 9582    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9583}
 9584
 9585#[gpui::test]
 9586async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9587    init_test(cx, |_| {});
 9588
 9589    let mut cx = EditorTestContext::new(cx).await;
 9590
 9591    let diff_base = r#"
 9592        use some::mod;
 9593
 9594        const A: u32 = 42;
 9595
 9596        fn main() {
 9597            println!("hello");
 9598
 9599            println!("world");
 9600        }
 9601        "#
 9602    .unindent();
 9603
 9604    // Edits are modified, removed, modified, added
 9605    cx.set_state(
 9606        &r#"
 9607        use some::modified;
 9608
 9609        ˇ
 9610        fn main() {
 9611            println!("hello there");
 9612
 9613            println!("around the");
 9614            println!("world");
 9615        }
 9616        "#
 9617        .unindent(),
 9618    );
 9619
 9620    cx.set_diff_base(Some(&diff_base));
 9621    executor.run_until_parked();
 9622
 9623    cx.update_editor(|editor, cx| {
 9624        //Wrap around the bottom of the buffer
 9625        for _ in 0..3 {
 9626            editor.go_to_hunk(&GoToHunk, cx);
 9627        }
 9628    });
 9629
 9630    cx.assert_editor_state(
 9631        &r#"
 9632        ˇuse some::modified;
 9633
 9634
 9635        fn main() {
 9636            println!("hello there");
 9637
 9638            println!("around the");
 9639            println!("world");
 9640        }
 9641        "#
 9642        .unindent(),
 9643    );
 9644
 9645    cx.update_editor(|editor, cx| {
 9646        //Wrap around the top of the buffer
 9647        for _ in 0..2 {
 9648            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9649        }
 9650    });
 9651
 9652    cx.assert_editor_state(
 9653        &r#"
 9654        use some::modified;
 9655
 9656
 9657        fn main() {
 9658        ˇ    println!("hello there");
 9659
 9660            println!("around the");
 9661            println!("world");
 9662        }
 9663        "#
 9664        .unindent(),
 9665    );
 9666
 9667    cx.update_editor(|editor, cx| {
 9668        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9669    });
 9670
 9671    cx.assert_editor_state(
 9672        &r#"
 9673        use some::modified;
 9674
 9675        ˇ
 9676        fn main() {
 9677            println!("hello there");
 9678
 9679            println!("around the");
 9680            println!("world");
 9681        }
 9682        "#
 9683        .unindent(),
 9684    );
 9685
 9686    cx.update_editor(|editor, cx| {
 9687        for _ in 0..3 {
 9688            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9689        }
 9690    });
 9691
 9692    cx.assert_editor_state(
 9693        &r#"
 9694        use some::modified;
 9695
 9696
 9697        fn main() {
 9698        ˇ    println!("hello there");
 9699
 9700            println!("around the");
 9701            println!("world");
 9702        }
 9703        "#
 9704        .unindent(),
 9705    );
 9706
 9707    cx.update_editor(|editor, cx| {
 9708        editor.fold(&Fold, cx);
 9709
 9710        //Make sure that the fold only gets one hunk
 9711        for _ in 0..4 {
 9712            editor.go_to_hunk(&GoToHunk, cx);
 9713        }
 9714    });
 9715
 9716    cx.assert_editor_state(
 9717        &r#"
 9718        ˇuse some::modified;
 9719
 9720
 9721        fn main() {
 9722            println!("hello there");
 9723
 9724            println!("around the");
 9725            println!("world");
 9726        }
 9727        "#
 9728        .unindent(),
 9729    );
 9730}
 9731
 9732#[test]
 9733fn test_split_words() {
 9734    fn split(text: &str) -> Vec<&str> {
 9735        split_words(text).collect()
 9736    }
 9737
 9738    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9739    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9740    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9741    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9742    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9743    assert_eq!(split("helloworld"), &["helloworld"]);
 9744
 9745    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9746}
 9747
 9748#[gpui::test]
 9749async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9750    init_test(cx, |_| {});
 9751
 9752    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9753    let mut assert = |before, after| {
 9754        let _state_context = cx.set_state(before);
 9755        cx.update_editor(|editor, cx| {
 9756            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9757        });
 9758        cx.assert_editor_state(after);
 9759    };
 9760
 9761    // Outside bracket jumps to outside of matching bracket
 9762    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9763    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9764
 9765    // Inside bracket jumps to inside of matching bracket
 9766    assert("console.log(ˇvar);", "console.log(varˇ);");
 9767    assert("console.log(varˇ);", "console.log(ˇvar);");
 9768
 9769    // When outside a bracket and inside, favor jumping to the inside bracket
 9770    assert(
 9771        "console.log('foo', [1, 2, 3]ˇ);",
 9772        "console.log(ˇ'foo', [1, 2, 3]);",
 9773    );
 9774    assert(
 9775        "console.log(ˇ'foo', [1, 2, 3]);",
 9776        "console.log('foo', [1, 2, 3]ˇ);",
 9777    );
 9778
 9779    // Bias forward if two options are equally likely
 9780    assert(
 9781        "let result = curried_fun()ˇ();",
 9782        "let result = curried_fun()()ˇ;",
 9783    );
 9784
 9785    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9786    assert(
 9787        indoc! {"
 9788            function test() {
 9789                console.log('test')ˇ
 9790            }"},
 9791        indoc! {"
 9792            function test() {
 9793                console.logˇ('test')
 9794            }"},
 9795    );
 9796}
 9797
 9798#[gpui::test]
 9799async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9800    init_test(cx, |_| {});
 9801
 9802    let fs = FakeFs::new(cx.executor());
 9803    fs.insert_tree(
 9804        "/a",
 9805        json!({
 9806            "main.rs": "fn main() { let a = 5; }",
 9807            "other.rs": "// Test file",
 9808        }),
 9809    )
 9810    .await;
 9811    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9812
 9813    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9814    language_registry.add(Arc::new(Language::new(
 9815        LanguageConfig {
 9816            name: "Rust".into(),
 9817            matcher: LanguageMatcher {
 9818                path_suffixes: vec!["rs".to_string()],
 9819                ..Default::default()
 9820            },
 9821            brackets: BracketPairConfig {
 9822                pairs: vec![BracketPair {
 9823                    start: "{".to_string(),
 9824                    end: "}".to_string(),
 9825                    close: true,
 9826                    surround: true,
 9827                    newline: true,
 9828                }],
 9829                disabled_scopes_by_bracket_ix: Vec::new(),
 9830            },
 9831            ..Default::default()
 9832        },
 9833        Some(tree_sitter_rust::LANGUAGE.into()),
 9834    )));
 9835    let mut fake_servers = language_registry.register_fake_lsp(
 9836        "Rust",
 9837        FakeLspAdapter {
 9838            capabilities: lsp::ServerCapabilities {
 9839                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9840                    first_trigger_character: "{".to_string(),
 9841                    more_trigger_character: None,
 9842                }),
 9843                ..Default::default()
 9844            },
 9845            ..Default::default()
 9846        },
 9847    );
 9848
 9849    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9850
 9851    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9852
 9853    let worktree_id = workspace
 9854        .update(cx, |workspace, cx| {
 9855            workspace.project().update(cx, |project, cx| {
 9856                project.worktrees(cx).next().unwrap().read(cx).id()
 9857            })
 9858        })
 9859        .unwrap();
 9860
 9861    let buffer = project
 9862        .update(cx, |project, cx| {
 9863            project.open_local_buffer("/a/main.rs", cx)
 9864        })
 9865        .await
 9866        .unwrap();
 9867    cx.executor().run_until_parked();
 9868    cx.executor().start_waiting();
 9869    let fake_server = fake_servers.next().await.unwrap();
 9870    let editor_handle = workspace
 9871        .update(cx, |workspace, cx| {
 9872            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9873        })
 9874        .unwrap()
 9875        .await
 9876        .unwrap()
 9877        .downcast::<Editor>()
 9878        .unwrap();
 9879
 9880    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9881        assert_eq!(
 9882            params.text_document_position.text_document.uri,
 9883            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9884        );
 9885        assert_eq!(
 9886            params.text_document_position.position,
 9887            lsp::Position::new(0, 21),
 9888        );
 9889
 9890        Ok(Some(vec![lsp::TextEdit {
 9891            new_text: "]".to_string(),
 9892            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9893        }]))
 9894    });
 9895
 9896    editor_handle.update(cx, |editor, cx| {
 9897        editor.focus(cx);
 9898        editor.change_selections(None, cx, |s| {
 9899            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9900        });
 9901        editor.handle_input("{", cx);
 9902    });
 9903
 9904    cx.executor().run_until_parked();
 9905
 9906    buffer.update(cx, |buffer, _| {
 9907        assert_eq!(
 9908            buffer.text(),
 9909            "fn main() { let a = {5}; }",
 9910            "No extra braces from on type formatting should appear in the buffer"
 9911        )
 9912    });
 9913}
 9914
 9915#[gpui::test]
 9916async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9917    init_test(cx, |_| {});
 9918
 9919    let fs = FakeFs::new(cx.executor());
 9920    fs.insert_tree(
 9921        "/a",
 9922        json!({
 9923            "main.rs": "fn main() { let a = 5; }",
 9924            "other.rs": "// Test file",
 9925        }),
 9926    )
 9927    .await;
 9928
 9929    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9930
 9931    let server_restarts = Arc::new(AtomicUsize::new(0));
 9932    let closure_restarts = Arc::clone(&server_restarts);
 9933    let language_server_name = "test language server";
 9934    let language_name: LanguageName = "Rust".into();
 9935
 9936    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9937    language_registry.add(Arc::new(Language::new(
 9938        LanguageConfig {
 9939            name: language_name.clone(),
 9940            matcher: LanguageMatcher {
 9941                path_suffixes: vec!["rs".to_string()],
 9942                ..Default::default()
 9943            },
 9944            ..Default::default()
 9945        },
 9946        Some(tree_sitter_rust::LANGUAGE.into()),
 9947    )));
 9948    let mut fake_servers = language_registry.register_fake_lsp(
 9949        "Rust",
 9950        FakeLspAdapter {
 9951            name: language_server_name,
 9952            initialization_options: Some(json!({
 9953                "testOptionValue": true
 9954            })),
 9955            initializer: Some(Box::new(move |fake_server| {
 9956                let task_restarts = Arc::clone(&closure_restarts);
 9957                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9958                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9959                    futures::future::ready(Ok(()))
 9960                });
 9961            })),
 9962            ..Default::default()
 9963        },
 9964    );
 9965
 9966    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9967    let _buffer = project
 9968        .update(cx, |project, cx| {
 9969            project.open_local_buffer("/a/main.rs", cx)
 9970        })
 9971        .await
 9972        .unwrap();
 9973    let _fake_server = fake_servers.next().await.unwrap();
 9974    update_test_language_settings(cx, |language_settings| {
 9975        language_settings.languages.insert(
 9976            language_name.clone(),
 9977            LanguageSettingsContent {
 9978                tab_size: NonZeroU32::new(8),
 9979                ..Default::default()
 9980            },
 9981        );
 9982    });
 9983    cx.executor().run_until_parked();
 9984    assert_eq!(
 9985        server_restarts.load(atomic::Ordering::Acquire),
 9986        0,
 9987        "Should not restart LSP server on an unrelated change"
 9988    );
 9989
 9990    update_test_project_settings(cx, |project_settings| {
 9991        project_settings.lsp.insert(
 9992            "Some other server name".into(),
 9993            LspSettings {
 9994                binary: None,
 9995                settings: None,
 9996                initialization_options: Some(json!({
 9997                    "some other init value": false
 9998                })),
 9999            },
10000        );
10001    });
10002    cx.executor().run_until_parked();
10003    assert_eq!(
10004        server_restarts.load(atomic::Ordering::Acquire),
10005        0,
10006        "Should not restart LSP server on an unrelated LSP settings change"
10007    );
10008
10009    update_test_project_settings(cx, |project_settings| {
10010        project_settings.lsp.insert(
10011            language_server_name.into(),
10012            LspSettings {
10013                binary: None,
10014                settings: None,
10015                initialization_options: Some(json!({
10016                    "anotherInitValue": false
10017                })),
10018            },
10019        );
10020    });
10021    cx.executor().run_until_parked();
10022    assert_eq!(
10023        server_restarts.load(atomic::Ordering::Acquire),
10024        1,
10025        "Should restart LSP server on a related LSP settings change"
10026    );
10027
10028    update_test_project_settings(cx, |project_settings| {
10029        project_settings.lsp.insert(
10030            language_server_name.into(),
10031            LspSettings {
10032                binary: None,
10033                settings: None,
10034                initialization_options: Some(json!({
10035                    "anotherInitValue": false
10036                })),
10037            },
10038        );
10039    });
10040    cx.executor().run_until_parked();
10041    assert_eq!(
10042        server_restarts.load(atomic::Ordering::Acquire),
10043        1,
10044        "Should not restart LSP server on a related LSP settings change that is the same"
10045    );
10046
10047    update_test_project_settings(cx, |project_settings| {
10048        project_settings.lsp.insert(
10049            language_server_name.into(),
10050            LspSettings {
10051                binary: None,
10052                settings: None,
10053                initialization_options: None,
10054            },
10055        );
10056    });
10057    cx.executor().run_until_parked();
10058    assert_eq!(
10059        server_restarts.load(atomic::Ordering::Acquire),
10060        2,
10061        "Should restart LSP server on another related LSP settings change"
10062    );
10063}
10064
10065#[gpui::test]
10066async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10067    init_test(cx, |_| {});
10068
10069    let mut cx = EditorLspTestContext::new_rust(
10070        lsp::ServerCapabilities {
10071            completion_provider: Some(lsp::CompletionOptions {
10072                trigger_characters: Some(vec![".".to_string()]),
10073                resolve_provider: Some(true),
10074                ..Default::default()
10075            }),
10076            ..Default::default()
10077        },
10078        cx,
10079    )
10080    .await;
10081
10082    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10083    cx.simulate_keystroke(".");
10084    let completion_item = lsp::CompletionItem {
10085        label: "some".into(),
10086        kind: Some(lsp::CompletionItemKind::SNIPPET),
10087        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10088        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10089            kind: lsp::MarkupKind::Markdown,
10090            value: "```rust\nSome(2)\n```".to_string(),
10091        })),
10092        deprecated: Some(false),
10093        sort_text: Some("fffffff2".to_string()),
10094        filter_text: Some("some".to_string()),
10095        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10096        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10097            range: lsp::Range {
10098                start: lsp::Position {
10099                    line: 0,
10100                    character: 22,
10101                },
10102                end: lsp::Position {
10103                    line: 0,
10104                    character: 22,
10105                },
10106            },
10107            new_text: "Some(2)".to_string(),
10108        })),
10109        additional_text_edits: Some(vec![lsp::TextEdit {
10110            range: lsp::Range {
10111                start: lsp::Position {
10112                    line: 0,
10113                    character: 20,
10114                },
10115                end: lsp::Position {
10116                    line: 0,
10117                    character: 22,
10118                },
10119            },
10120            new_text: "".to_string(),
10121        }]),
10122        ..Default::default()
10123    };
10124
10125    let closure_completion_item = completion_item.clone();
10126    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10127        let task_completion_item = closure_completion_item.clone();
10128        async move {
10129            Ok(Some(lsp::CompletionResponse::Array(vec![
10130                task_completion_item,
10131            ])))
10132        }
10133    });
10134
10135    request.next().await;
10136
10137    cx.condition(|editor, _| editor.context_menu_visible())
10138        .await;
10139    let apply_additional_edits = cx.update_editor(|editor, cx| {
10140        editor
10141            .confirm_completion(&ConfirmCompletion::default(), cx)
10142            .unwrap()
10143    });
10144    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10145
10146    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10147        let task_completion_item = completion_item.clone();
10148        async move { Ok(task_completion_item) }
10149    })
10150    .next()
10151    .await
10152    .unwrap();
10153    apply_additional_edits.await.unwrap();
10154    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10155}
10156
10157#[gpui::test]
10158async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10159    init_test(cx, |_| {});
10160
10161    let mut cx = EditorLspTestContext::new(
10162        Language::new(
10163            LanguageConfig {
10164                matcher: LanguageMatcher {
10165                    path_suffixes: vec!["jsx".into()],
10166                    ..Default::default()
10167                },
10168                overrides: [(
10169                    "element".into(),
10170                    LanguageConfigOverride {
10171                        word_characters: Override::Set(['-'].into_iter().collect()),
10172                        ..Default::default()
10173                    },
10174                )]
10175                .into_iter()
10176                .collect(),
10177                ..Default::default()
10178            },
10179            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10180        )
10181        .with_override_query("(jsx_self_closing_element) @element")
10182        .unwrap(),
10183        lsp::ServerCapabilities {
10184            completion_provider: Some(lsp::CompletionOptions {
10185                trigger_characters: Some(vec![":".to_string()]),
10186                ..Default::default()
10187            }),
10188            ..Default::default()
10189        },
10190        cx,
10191    )
10192    .await;
10193
10194    cx.lsp
10195        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10196            Ok(Some(lsp::CompletionResponse::Array(vec![
10197                lsp::CompletionItem {
10198                    label: "bg-blue".into(),
10199                    ..Default::default()
10200                },
10201                lsp::CompletionItem {
10202                    label: "bg-red".into(),
10203                    ..Default::default()
10204                },
10205                lsp::CompletionItem {
10206                    label: "bg-yellow".into(),
10207                    ..Default::default()
10208                },
10209            ])))
10210        });
10211
10212    cx.set_state(r#"<p class="bgˇ" />"#);
10213
10214    // Trigger completion when typing a dash, because the dash is an extra
10215    // word character in the 'element' scope, which contains the cursor.
10216    cx.simulate_keystroke("-");
10217    cx.executor().run_until_parked();
10218    cx.update_editor(|editor, _| {
10219        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10220            assert_eq!(
10221                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10222                &["bg-red", "bg-blue", "bg-yellow"]
10223            );
10224        } else {
10225            panic!("expected completion menu to be open");
10226        }
10227    });
10228
10229    cx.simulate_keystroke("l");
10230    cx.executor().run_until_parked();
10231    cx.update_editor(|editor, _| {
10232        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10233            assert_eq!(
10234                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10235                &["bg-blue", "bg-yellow"]
10236            );
10237        } else {
10238            panic!("expected completion menu to be open");
10239        }
10240    });
10241
10242    // When filtering completions, consider the character after the '-' to
10243    // be the start of a subword.
10244    cx.set_state(r#"<p class="yelˇ" />"#);
10245    cx.simulate_keystroke("l");
10246    cx.executor().run_until_parked();
10247    cx.update_editor(|editor, _| {
10248        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10249            assert_eq!(
10250                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10251                &["bg-yellow"]
10252            );
10253        } else {
10254            panic!("expected completion menu to be open");
10255        }
10256    });
10257}
10258
10259#[gpui::test]
10260async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10261    init_test(cx, |settings| {
10262        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10263            FormatterList(vec![Formatter::Prettier].into()),
10264        ))
10265    });
10266
10267    let fs = FakeFs::new(cx.executor());
10268    fs.insert_file("/file.ts", Default::default()).await;
10269
10270    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10271    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10272
10273    language_registry.add(Arc::new(Language::new(
10274        LanguageConfig {
10275            name: "TypeScript".into(),
10276            matcher: LanguageMatcher {
10277                path_suffixes: vec!["ts".to_string()],
10278                ..Default::default()
10279            },
10280            ..Default::default()
10281        },
10282        Some(tree_sitter_rust::LANGUAGE.into()),
10283    )));
10284    update_test_language_settings(cx, |settings| {
10285        settings.defaults.prettier = Some(PrettierSettings {
10286            allowed: true,
10287            ..PrettierSettings::default()
10288        });
10289    });
10290
10291    let test_plugin = "test_plugin";
10292    let _ = language_registry.register_fake_lsp(
10293        "TypeScript",
10294        FakeLspAdapter {
10295            prettier_plugins: vec![test_plugin],
10296            ..Default::default()
10297        },
10298    );
10299
10300    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10301    let buffer = project
10302        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10303        .await
10304        .unwrap();
10305
10306    let buffer_text = "one\ntwo\nthree\n";
10307    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10308    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10309    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10310
10311    editor
10312        .update(cx, |editor, cx| {
10313            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10314        })
10315        .unwrap()
10316        .await;
10317    assert_eq!(
10318        editor.update(cx, |editor, cx| editor.text(cx)),
10319        buffer_text.to_string() + prettier_format_suffix,
10320        "Test prettier formatting was not applied to the original buffer text",
10321    );
10322
10323    update_test_language_settings(cx, |settings| {
10324        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10325    });
10326    let format = editor.update(cx, |editor, cx| {
10327        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10328    });
10329    format.await.unwrap();
10330    assert_eq!(
10331        editor.update(cx, |editor, cx| editor.text(cx)),
10332        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10333        "Autoformatting (via test prettier) was not applied to the original buffer text",
10334    );
10335}
10336
10337#[gpui::test]
10338async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10339    init_test(cx, |_| {});
10340    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10341    let base_text = indoc! {r#"struct Row;
10342struct Row1;
10343struct Row2;
10344
10345struct Row4;
10346struct Row5;
10347struct Row6;
10348
10349struct Row8;
10350struct Row9;
10351struct Row10;"#};
10352
10353    // When addition hunks are not adjacent to carets, no hunk revert is performed
10354    assert_hunk_revert(
10355        indoc! {r#"struct Row;
10356                   struct Row1;
10357                   struct Row1.1;
10358                   struct Row1.2;
10359                   struct Row2;ˇ
10360
10361                   struct Row4;
10362                   struct Row5;
10363                   struct Row6;
10364
10365                   struct Row8;
10366                   ˇstruct Row9;
10367                   struct Row9.1;
10368                   struct Row9.2;
10369                   struct Row9.3;
10370                   struct Row10;"#},
10371        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10372        indoc! {r#"struct Row;
10373                   struct Row1;
10374                   struct Row1.1;
10375                   struct Row1.2;
10376                   struct Row2;ˇ
10377
10378                   struct Row4;
10379                   struct Row5;
10380                   struct Row6;
10381
10382                   struct Row8;
10383                   ˇstruct Row9;
10384                   struct Row9.1;
10385                   struct Row9.2;
10386                   struct Row9.3;
10387                   struct Row10;"#},
10388        base_text,
10389        &mut cx,
10390    );
10391    // Same for selections
10392    assert_hunk_revert(
10393        indoc! {r#"struct Row;
10394                   struct Row1;
10395                   struct Row2;
10396                   struct Row2.1;
10397                   struct Row2.2;
10398                   «ˇ
10399                   struct Row4;
10400                   struct» Row5;
10401                   «struct Row6;
10402                   ˇ»
10403                   struct Row9.1;
10404                   struct Row9.2;
10405                   struct Row9.3;
10406                   struct Row8;
10407                   struct Row9;
10408                   struct Row10;"#},
10409        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10410        indoc! {r#"struct Row;
10411                   struct Row1;
10412                   struct Row2;
10413                   struct Row2.1;
10414                   struct Row2.2;
10415                   «ˇ
10416                   struct Row4;
10417                   struct» Row5;
10418                   «struct Row6;
10419                   ˇ»
10420                   struct Row9.1;
10421                   struct Row9.2;
10422                   struct Row9.3;
10423                   struct Row8;
10424                   struct Row9;
10425                   struct Row10;"#},
10426        base_text,
10427        &mut cx,
10428    );
10429
10430    // When carets and selections intersect the addition hunks, those are reverted.
10431    // Adjacent carets got merged.
10432    assert_hunk_revert(
10433        indoc! {r#"struct Row;
10434                   ˇ// something on the top
10435                   struct Row1;
10436                   struct Row2;
10437                   struct Roˇw3.1;
10438                   struct Row2.2;
10439                   struct Row2.3;ˇ
10440
10441                   struct Row4;
10442                   struct ˇRow5.1;
10443                   struct Row5.2;
10444                   struct «Rowˇ»5.3;
10445                   struct Row5;
10446                   struct Row6;
10447                   ˇ
10448                   struct Row9.1;
10449                   struct «Rowˇ»9.2;
10450                   struct «ˇRow»9.3;
10451                   struct Row8;
10452                   struct Row9;
10453                   «ˇ// something on bottom»
10454                   struct Row10;"#},
10455        vec![
10456            DiffHunkStatus::Added,
10457            DiffHunkStatus::Added,
10458            DiffHunkStatus::Added,
10459            DiffHunkStatus::Added,
10460            DiffHunkStatus::Added,
10461        ],
10462        indoc! {r#"struct Row;
10463                   ˇstruct Row1;
10464                   struct Row2;
10465                   ˇ
10466                   struct Row4;
10467                   ˇstruct Row5;
10468                   struct Row6;
10469                   ˇ
10470                   ˇstruct Row8;
10471                   struct Row9;
10472                   ˇstruct Row10;"#},
10473        base_text,
10474        &mut cx,
10475    );
10476}
10477
10478#[gpui::test]
10479async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10480    init_test(cx, |_| {});
10481    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10482    let base_text = indoc! {r#"struct Row;
10483struct Row1;
10484struct Row2;
10485
10486struct Row4;
10487struct Row5;
10488struct Row6;
10489
10490struct Row8;
10491struct Row9;
10492struct Row10;"#};
10493
10494    // Modification hunks behave the same as the addition ones.
10495    assert_hunk_revert(
10496        indoc! {r#"struct Row;
10497                   struct Row1;
10498                   struct Row33;
10499                   ˇ
10500                   struct Row4;
10501                   struct Row5;
10502                   struct Row6;
10503                   ˇ
10504                   struct Row99;
10505                   struct Row9;
10506                   struct Row10;"#},
10507        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10508        indoc! {r#"struct Row;
10509                   struct Row1;
10510                   struct Row33;
10511                   ˇ
10512                   struct Row4;
10513                   struct Row5;
10514                   struct Row6;
10515                   ˇ
10516                   struct Row99;
10517                   struct Row9;
10518                   struct Row10;"#},
10519        base_text,
10520        &mut cx,
10521    );
10522    assert_hunk_revert(
10523        indoc! {r#"struct Row;
10524                   struct Row1;
10525                   struct Row33;
10526                   «ˇ
10527                   struct Row4;
10528                   struct» Row5;
10529                   «struct Row6;
10530                   ˇ»
10531                   struct Row99;
10532                   struct Row9;
10533                   struct Row10;"#},
10534        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10535        indoc! {r#"struct Row;
10536                   struct Row1;
10537                   struct Row33;
10538                   «ˇ
10539                   struct Row4;
10540                   struct» Row5;
10541                   «struct Row6;
10542                   ˇ»
10543                   struct Row99;
10544                   struct Row9;
10545                   struct Row10;"#},
10546        base_text,
10547        &mut cx,
10548    );
10549
10550    assert_hunk_revert(
10551        indoc! {r#"ˇstruct Row1.1;
10552                   struct Row1;
10553                   «ˇstr»uct Row22;
10554
10555                   struct ˇRow44;
10556                   struct Row5;
10557                   struct «Rˇ»ow66;ˇ
10558
10559                   «struˇ»ct Row88;
10560                   struct Row9;
10561                   struct Row1011;ˇ"#},
10562        vec![
10563            DiffHunkStatus::Modified,
10564            DiffHunkStatus::Modified,
10565            DiffHunkStatus::Modified,
10566            DiffHunkStatus::Modified,
10567            DiffHunkStatus::Modified,
10568            DiffHunkStatus::Modified,
10569        ],
10570        indoc! {r#"struct Row;
10571                   ˇstruct Row1;
10572                   struct Row2;
10573                   ˇ
10574                   struct Row4;
10575                   ˇstruct Row5;
10576                   struct Row6;
10577                   ˇ
10578                   struct Row8;
10579                   ˇstruct Row9;
10580                   struct Row10;ˇ"#},
10581        base_text,
10582        &mut cx,
10583    );
10584}
10585
10586#[gpui::test]
10587async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10588    init_test(cx, |_| {});
10589    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10590    let base_text = indoc! {r#"struct Row;
10591struct Row1;
10592struct Row2;
10593
10594struct Row4;
10595struct Row5;
10596struct Row6;
10597
10598struct Row8;
10599struct Row9;
10600struct Row10;"#};
10601
10602    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10603    assert_hunk_revert(
10604        indoc! {r#"struct Row;
10605                   struct Row2;
10606
10607                   ˇstruct Row4;
10608                   struct Row5;
10609                   struct Row6;
10610                   ˇ
10611                   struct Row8;
10612                   struct Row10;"#},
10613        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10614        indoc! {r#"struct Row;
10615                   struct Row2;
10616
10617                   ˇstruct Row4;
10618                   struct Row5;
10619                   struct Row6;
10620                   ˇ
10621                   struct Row8;
10622                   struct Row10;"#},
10623        base_text,
10624        &mut cx,
10625    );
10626    assert_hunk_revert(
10627        indoc! {r#"struct Row;
10628                   struct Row2;
10629
10630                   «ˇstruct Row4;
10631                   struct» Row5;
10632                   «struct Row6;
10633                   ˇ»
10634                   struct Row8;
10635                   struct Row10;"#},
10636        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10637        indoc! {r#"struct Row;
10638                   struct Row2;
10639
10640                   «ˇstruct Row4;
10641                   struct» Row5;
10642                   «struct Row6;
10643                   ˇ»
10644                   struct Row8;
10645                   struct Row10;"#},
10646        base_text,
10647        &mut cx,
10648    );
10649
10650    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10651    assert_hunk_revert(
10652        indoc! {r#"struct Row;
10653                   ˇstruct Row2;
10654
10655                   struct Row4;
10656                   struct Row5;
10657                   struct Row6;
10658
10659                   struct Row8;ˇ
10660                   struct Row10;"#},
10661        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10662        indoc! {r#"struct Row;
10663                   struct Row1;
10664                   ˇstruct Row2;
10665
10666                   struct Row4;
10667                   struct Row5;
10668                   struct Row6;
10669
10670                   struct Row8;ˇ
10671                   struct Row9;
10672                   struct Row10;"#},
10673        base_text,
10674        &mut cx,
10675    );
10676    assert_hunk_revert(
10677        indoc! {r#"struct Row;
10678                   struct Row2«ˇ;
10679                   struct Row4;
10680                   struct» Row5;
10681                   «struct Row6;
10682
10683                   struct Row8;ˇ»
10684                   struct Row10;"#},
10685        vec![
10686            DiffHunkStatus::Removed,
10687            DiffHunkStatus::Removed,
10688            DiffHunkStatus::Removed,
10689        ],
10690        indoc! {r#"struct Row;
10691                   struct Row1;
10692                   struct Row2«ˇ;
10693
10694                   struct Row4;
10695                   struct» Row5;
10696                   «struct Row6;
10697
10698                   struct Row8;ˇ»
10699                   struct Row9;
10700                   struct Row10;"#},
10701        base_text,
10702        &mut cx,
10703    );
10704}
10705
10706#[gpui::test]
10707async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10708    init_test(cx, |_| {});
10709
10710    let cols = 4;
10711    let rows = 10;
10712    let sample_text_1 = sample_text(rows, cols, 'a');
10713    assert_eq!(
10714        sample_text_1,
10715        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10716    );
10717    let sample_text_2 = sample_text(rows, cols, 'l');
10718    assert_eq!(
10719        sample_text_2,
10720        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10721    );
10722    let sample_text_3 = sample_text(rows, cols, 'v');
10723    assert_eq!(
10724        sample_text_3,
10725        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10726    );
10727
10728    fn diff_every_buffer_row(
10729        buffer: &Model<Buffer>,
10730        sample_text: String,
10731        cols: usize,
10732        cx: &mut gpui::TestAppContext,
10733    ) {
10734        // revert first character in each row, creating one large diff hunk per buffer
10735        let is_first_char = |offset: usize| offset % cols == 0;
10736        buffer.update(cx, |buffer, cx| {
10737            buffer.set_text(
10738                sample_text
10739                    .chars()
10740                    .enumerate()
10741                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10742                    .collect::<String>(),
10743                cx,
10744            );
10745            buffer.set_diff_base(Some(sample_text), cx);
10746        });
10747        cx.executor().run_until_parked();
10748    }
10749
10750    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10751    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10752
10753    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10754    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10755
10756    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10757    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10758
10759    let multibuffer = cx.new_model(|cx| {
10760        let mut multibuffer = MultiBuffer::new(ReadWrite);
10761        multibuffer.push_excerpts(
10762            buffer_1.clone(),
10763            [
10764                ExcerptRange {
10765                    context: Point::new(0, 0)..Point::new(3, 0),
10766                    primary: None,
10767                },
10768                ExcerptRange {
10769                    context: Point::new(5, 0)..Point::new(7, 0),
10770                    primary: None,
10771                },
10772                ExcerptRange {
10773                    context: Point::new(9, 0)..Point::new(10, 4),
10774                    primary: None,
10775                },
10776            ],
10777            cx,
10778        );
10779        multibuffer.push_excerpts(
10780            buffer_2.clone(),
10781            [
10782                ExcerptRange {
10783                    context: Point::new(0, 0)..Point::new(3, 0),
10784                    primary: None,
10785                },
10786                ExcerptRange {
10787                    context: Point::new(5, 0)..Point::new(7, 0),
10788                    primary: None,
10789                },
10790                ExcerptRange {
10791                    context: Point::new(9, 0)..Point::new(10, 4),
10792                    primary: None,
10793                },
10794            ],
10795            cx,
10796        );
10797        multibuffer.push_excerpts(
10798            buffer_3.clone(),
10799            [
10800                ExcerptRange {
10801                    context: Point::new(0, 0)..Point::new(3, 0),
10802                    primary: None,
10803                },
10804                ExcerptRange {
10805                    context: Point::new(5, 0)..Point::new(7, 0),
10806                    primary: None,
10807                },
10808                ExcerptRange {
10809                    context: Point::new(9, 0)..Point::new(10, 4),
10810                    primary: None,
10811                },
10812            ],
10813            cx,
10814        );
10815        multibuffer
10816    });
10817
10818    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10819    editor.update(cx, |editor, cx| {
10820        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");
10821        editor.select_all(&SelectAll, cx);
10822        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10823    });
10824    cx.executor().run_until_parked();
10825    // When all ranges are selected, all buffer hunks are reverted.
10826    editor.update(cx, |editor, cx| {
10827        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");
10828    });
10829    buffer_1.update(cx, |buffer, _| {
10830        assert_eq!(buffer.text(), sample_text_1);
10831    });
10832    buffer_2.update(cx, |buffer, _| {
10833        assert_eq!(buffer.text(), sample_text_2);
10834    });
10835    buffer_3.update(cx, |buffer, _| {
10836        assert_eq!(buffer.text(), sample_text_3);
10837    });
10838
10839    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10840    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10841    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10842    editor.update(cx, |editor, cx| {
10843        editor.change_selections(None, cx, |s| {
10844            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10845        });
10846        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10847    });
10848    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10849    // but not affect buffer_2 and its related excerpts.
10850    editor.update(cx, |editor, cx| {
10851        assert_eq!(
10852            editor.text(cx),
10853            "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"
10854        );
10855    });
10856    buffer_1.update(cx, |buffer, _| {
10857        assert_eq!(buffer.text(), sample_text_1);
10858    });
10859    buffer_2.update(cx, |buffer, _| {
10860        assert_eq!(
10861            buffer.text(),
10862            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10863        );
10864    });
10865    buffer_3.update(cx, |buffer, _| {
10866        assert_eq!(
10867            buffer.text(),
10868            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10869        );
10870    });
10871}
10872
10873#[gpui::test]
10874async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10875    init_test(cx, |_| {});
10876
10877    let cols = 4;
10878    let rows = 10;
10879    let sample_text_1 = sample_text(rows, cols, 'a');
10880    assert_eq!(
10881        sample_text_1,
10882        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10883    );
10884    let sample_text_2 = sample_text(rows, cols, 'l');
10885    assert_eq!(
10886        sample_text_2,
10887        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10888    );
10889    let sample_text_3 = sample_text(rows, cols, 'v');
10890    assert_eq!(
10891        sample_text_3,
10892        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10893    );
10894
10895    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10896    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10897    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10898
10899    let multi_buffer = cx.new_model(|cx| {
10900        let mut multibuffer = MultiBuffer::new(ReadWrite);
10901        multibuffer.push_excerpts(
10902            buffer_1.clone(),
10903            [
10904                ExcerptRange {
10905                    context: Point::new(0, 0)..Point::new(3, 0),
10906                    primary: None,
10907                },
10908                ExcerptRange {
10909                    context: Point::new(5, 0)..Point::new(7, 0),
10910                    primary: None,
10911                },
10912                ExcerptRange {
10913                    context: Point::new(9, 0)..Point::new(10, 4),
10914                    primary: None,
10915                },
10916            ],
10917            cx,
10918        );
10919        multibuffer.push_excerpts(
10920            buffer_2.clone(),
10921            [
10922                ExcerptRange {
10923                    context: Point::new(0, 0)..Point::new(3, 0),
10924                    primary: None,
10925                },
10926                ExcerptRange {
10927                    context: Point::new(5, 0)..Point::new(7, 0),
10928                    primary: None,
10929                },
10930                ExcerptRange {
10931                    context: Point::new(9, 0)..Point::new(10, 4),
10932                    primary: None,
10933                },
10934            ],
10935            cx,
10936        );
10937        multibuffer.push_excerpts(
10938            buffer_3.clone(),
10939            [
10940                ExcerptRange {
10941                    context: Point::new(0, 0)..Point::new(3, 0),
10942                    primary: None,
10943                },
10944                ExcerptRange {
10945                    context: Point::new(5, 0)..Point::new(7, 0),
10946                    primary: None,
10947                },
10948                ExcerptRange {
10949                    context: Point::new(9, 0)..Point::new(10, 4),
10950                    primary: None,
10951                },
10952            ],
10953            cx,
10954        );
10955        multibuffer
10956    });
10957
10958    let fs = FakeFs::new(cx.executor());
10959    fs.insert_tree(
10960        "/a",
10961        json!({
10962            "main.rs": sample_text_1,
10963            "other.rs": sample_text_2,
10964            "lib.rs": sample_text_3,
10965        }),
10966    )
10967    .await;
10968    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10969    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10970    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10971    let multi_buffer_editor = cx.new_view(|cx| {
10972        Editor::new(
10973            EditorMode::Full,
10974            multi_buffer,
10975            Some(project.clone()),
10976            true,
10977            cx,
10978        )
10979    });
10980    let multibuffer_item_id = workspace
10981        .update(cx, |workspace, cx| {
10982            assert!(
10983                workspace.active_item(cx).is_none(),
10984                "active item should be None before the first item is added"
10985            );
10986            workspace.add_item_to_active_pane(
10987                Box::new(multi_buffer_editor.clone()),
10988                None,
10989                true,
10990                cx,
10991            );
10992            let active_item = workspace
10993                .active_item(cx)
10994                .expect("should have an active item after adding the multi buffer");
10995            assert!(
10996                !active_item.is_singleton(cx),
10997                "A multi buffer was expected to active after adding"
10998            );
10999            active_item.item_id()
11000        })
11001        .unwrap();
11002    cx.executor().run_until_parked();
11003
11004    multi_buffer_editor.update(cx, |editor, cx| {
11005        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11006        editor.open_excerpts(&OpenExcerpts, cx);
11007    });
11008    cx.executor().run_until_parked();
11009    let first_item_id = workspace
11010        .update(cx, |workspace, cx| {
11011            let active_item = workspace
11012                .active_item(cx)
11013                .expect("should have an active item after navigating into the 1st buffer");
11014            let first_item_id = active_item.item_id();
11015            assert_ne!(
11016                first_item_id, multibuffer_item_id,
11017                "Should navigate into the 1st buffer and activate it"
11018            );
11019            assert!(
11020                active_item.is_singleton(cx),
11021                "New active item should be a singleton buffer"
11022            );
11023            assert_eq!(
11024                active_item
11025                    .act_as::<Editor>(cx)
11026                    .expect("should have navigated into an editor for the 1st buffer")
11027                    .read(cx)
11028                    .text(cx),
11029                sample_text_1
11030            );
11031
11032            workspace
11033                .go_back(workspace.active_pane().downgrade(), cx)
11034                .detach_and_log_err(cx);
11035
11036            first_item_id
11037        })
11038        .unwrap();
11039    cx.executor().run_until_parked();
11040    workspace
11041        .update(cx, |workspace, cx| {
11042            let active_item = workspace
11043                .active_item(cx)
11044                .expect("should have an active item after navigating back");
11045            assert_eq!(
11046                active_item.item_id(),
11047                multibuffer_item_id,
11048                "Should navigate back to the multi buffer"
11049            );
11050            assert!(!active_item.is_singleton(cx));
11051        })
11052        .unwrap();
11053
11054    multi_buffer_editor.update(cx, |editor, cx| {
11055        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11056            s.select_ranges(Some(39..40))
11057        });
11058        editor.open_excerpts(&OpenExcerpts, cx);
11059    });
11060    cx.executor().run_until_parked();
11061    let second_item_id = workspace
11062        .update(cx, |workspace, cx| {
11063            let active_item = workspace
11064                .active_item(cx)
11065                .expect("should have an active item after navigating into the 2nd buffer");
11066            let second_item_id = active_item.item_id();
11067            assert_ne!(
11068                second_item_id, multibuffer_item_id,
11069                "Should navigate away from the multibuffer"
11070            );
11071            assert_ne!(
11072                second_item_id, first_item_id,
11073                "Should navigate into the 2nd buffer and activate it"
11074            );
11075            assert!(
11076                active_item.is_singleton(cx),
11077                "New active item should be a singleton buffer"
11078            );
11079            assert_eq!(
11080                active_item
11081                    .act_as::<Editor>(cx)
11082                    .expect("should have navigated into an editor")
11083                    .read(cx)
11084                    .text(cx),
11085                sample_text_2
11086            );
11087
11088            workspace
11089                .go_back(workspace.active_pane().downgrade(), cx)
11090                .detach_and_log_err(cx);
11091
11092            second_item_id
11093        })
11094        .unwrap();
11095    cx.executor().run_until_parked();
11096    workspace
11097        .update(cx, |workspace, cx| {
11098            let active_item = workspace
11099                .active_item(cx)
11100                .expect("should have an active item after navigating back from the 2nd buffer");
11101            assert_eq!(
11102                active_item.item_id(),
11103                multibuffer_item_id,
11104                "Should navigate back from the 2nd buffer to the multi buffer"
11105            );
11106            assert!(!active_item.is_singleton(cx));
11107        })
11108        .unwrap();
11109
11110    multi_buffer_editor.update(cx, |editor, cx| {
11111        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11112            s.select_ranges(Some(60..70))
11113        });
11114        editor.open_excerpts(&OpenExcerpts, cx);
11115    });
11116    cx.executor().run_until_parked();
11117    workspace
11118        .update(cx, |workspace, cx| {
11119            let active_item = workspace
11120                .active_item(cx)
11121                .expect("should have an active item after navigating into the 3rd buffer");
11122            let third_item_id = active_item.item_id();
11123            assert_ne!(
11124                third_item_id, multibuffer_item_id,
11125                "Should navigate into the 3rd buffer and activate it"
11126            );
11127            assert_ne!(third_item_id, first_item_id);
11128            assert_ne!(third_item_id, second_item_id);
11129            assert!(
11130                active_item.is_singleton(cx),
11131                "New active item should be a singleton buffer"
11132            );
11133            assert_eq!(
11134                active_item
11135                    .act_as::<Editor>(cx)
11136                    .expect("should have navigated into an editor")
11137                    .read(cx)
11138                    .text(cx),
11139                sample_text_3
11140            );
11141
11142            workspace
11143                .go_back(workspace.active_pane().downgrade(), cx)
11144                .detach_and_log_err(cx);
11145        })
11146        .unwrap();
11147    cx.executor().run_until_parked();
11148    workspace
11149        .update(cx, |workspace, cx| {
11150            let active_item = workspace
11151                .active_item(cx)
11152                .expect("should have an active item after navigating back from the 3rd buffer");
11153            assert_eq!(
11154                active_item.item_id(),
11155                multibuffer_item_id,
11156                "Should navigate back from the 3rd buffer to the multi buffer"
11157            );
11158            assert!(!active_item.is_singleton(cx));
11159        })
11160        .unwrap();
11161}
11162
11163#[gpui::test]
11164async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11165    init_test(cx, |_| {});
11166
11167    let mut cx = EditorTestContext::new(cx).await;
11168
11169    let diff_base = r#"
11170        use some::mod;
11171
11172        const A: u32 = 42;
11173
11174        fn main() {
11175            println!("hello");
11176
11177            println!("world");
11178        }
11179        "#
11180    .unindent();
11181
11182    cx.set_state(
11183        &r#"
11184        use some::modified;
11185
11186        ˇ
11187        fn main() {
11188            println!("hello there");
11189
11190            println!("around the");
11191            println!("world");
11192        }
11193        "#
11194        .unindent(),
11195    );
11196
11197    cx.set_diff_base(Some(&diff_base));
11198    executor.run_until_parked();
11199    let unexpanded_hunks = vec![
11200        (
11201            "use some::mod;\n".to_string(),
11202            DiffHunkStatus::Modified,
11203            DisplayRow(0)..DisplayRow(1),
11204        ),
11205        (
11206            "const A: u32 = 42;\n".to_string(),
11207            DiffHunkStatus::Removed,
11208            DisplayRow(2)..DisplayRow(2),
11209        ),
11210        (
11211            "    println!(\"hello\");\n".to_string(),
11212            DiffHunkStatus::Modified,
11213            DisplayRow(4)..DisplayRow(5),
11214        ),
11215        (
11216            "".to_string(),
11217            DiffHunkStatus::Added,
11218            DisplayRow(6)..DisplayRow(7),
11219        ),
11220    ];
11221    cx.update_editor(|editor, cx| {
11222        let snapshot = editor.snapshot(cx);
11223        let all_hunks = editor_hunks(editor, &snapshot, cx);
11224        assert_eq!(all_hunks, unexpanded_hunks);
11225    });
11226
11227    cx.update_editor(|editor, cx| {
11228        for _ in 0..4 {
11229            editor.go_to_hunk(&GoToHunk, cx);
11230            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11231        }
11232    });
11233    executor.run_until_parked();
11234    cx.assert_editor_state(
11235        &r#"
11236        use some::modified;
11237
11238        ˇ
11239        fn main() {
11240            println!("hello there");
11241
11242            println!("around the");
11243            println!("world");
11244        }
11245        "#
11246        .unindent(),
11247    );
11248    cx.update_editor(|editor, cx| {
11249        let snapshot = editor.snapshot(cx);
11250        let all_hunks = editor_hunks(editor, &snapshot, cx);
11251        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11252        assert_eq!(
11253            expanded_hunks_background_highlights(editor, cx),
11254            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
11255            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11256        );
11257        assert_eq!(
11258            all_hunks,
11259            vec![
11260                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
11261                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
11262                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
11263                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
11264            ],
11265            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11266            (from modified and removed hunks)"
11267        );
11268        assert_eq!(
11269            all_hunks, all_expanded_hunks,
11270            "Editor hunks should not change and all be expanded"
11271        );
11272    });
11273
11274    cx.update_editor(|editor, cx| {
11275        editor.cancel(&Cancel, cx);
11276
11277        let snapshot = editor.snapshot(cx);
11278        let all_hunks = editor_hunks(editor, &snapshot, cx);
11279        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11280        assert_eq!(
11281            expanded_hunks_background_highlights(editor, cx),
11282            Vec::new(),
11283            "After cancelling in editor, no git highlights should be left"
11284        );
11285        assert_eq!(
11286            all_expanded_hunks,
11287            Vec::new(),
11288            "After cancelling in editor, no hunks should be expanded"
11289        );
11290        assert_eq!(
11291            all_hunks, unexpanded_hunks,
11292            "After cancelling in editor, regular hunks' coordinates should get back to normal"
11293        );
11294    });
11295}
11296
11297#[gpui::test]
11298async fn test_toggled_diff_base_change(
11299    executor: BackgroundExecutor,
11300    cx: &mut gpui::TestAppContext,
11301) {
11302    init_test(cx, |_| {});
11303
11304    let mut cx = EditorTestContext::new(cx).await;
11305
11306    let diff_base = r#"
11307        use some::mod1;
11308        use some::mod2;
11309
11310        const A: u32 = 42;
11311        const B: u32 = 42;
11312        const C: u32 = 42;
11313
11314        fn main(ˇ) {
11315            println!("hello");
11316
11317            println!("world");
11318        }
11319        "#
11320    .unindent();
11321
11322    cx.set_state(
11323        &r#"
11324        use some::mod2;
11325
11326        const A: u32 = 42;
11327        const C: u32 = 42;
11328
11329        fn main(ˇ) {
11330            //println!("hello");
11331
11332            println!("world");
11333            //
11334            //
11335        }
11336        "#
11337        .unindent(),
11338    );
11339
11340    cx.set_diff_base(Some(&diff_base));
11341    executor.run_until_parked();
11342    cx.update_editor(|editor, cx| {
11343        let snapshot = editor.snapshot(cx);
11344        let all_hunks = editor_hunks(editor, &snapshot, cx);
11345        assert_eq!(
11346            all_hunks,
11347            vec![
11348                (
11349                    "use some::mod1;\n".to_string(),
11350                    DiffHunkStatus::Removed,
11351                    DisplayRow(0)..DisplayRow(0)
11352                ),
11353                (
11354                    "const B: u32 = 42;\n".to_string(),
11355                    DiffHunkStatus::Removed,
11356                    DisplayRow(3)..DisplayRow(3)
11357                ),
11358                (
11359                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11360                    DiffHunkStatus::Modified,
11361                    DisplayRow(5)..DisplayRow(7)
11362                ),
11363                (
11364                    "".to_string(),
11365                    DiffHunkStatus::Added,
11366                    DisplayRow(9)..DisplayRow(11)
11367                ),
11368            ]
11369        );
11370    });
11371
11372    cx.update_editor(|editor, cx| {
11373        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11374    });
11375    executor.run_until_parked();
11376    cx.assert_editor_state(
11377        &r#"
11378        use some::mod2;
11379
11380        const A: u32 = 42;
11381        const C: u32 = 42;
11382
11383        fn main(ˇ) {
11384            //println!("hello");
11385
11386            println!("world");
11387            //
11388            //
11389        }
11390        "#
11391        .unindent(),
11392    );
11393    cx.update_editor(|editor, cx| {
11394        let snapshot = editor.snapshot(cx);
11395        let all_hunks = editor_hunks(editor, &snapshot, cx);
11396        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11397        assert_eq!(
11398            expanded_hunks_background_highlights(editor, cx),
11399            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
11400            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11401        );
11402        assert_eq!(
11403            all_hunks,
11404            vec![
11405                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
11406                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
11407                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
11408                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
11409            ],
11410            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11411            (from modified and removed hunks)"
11412        );
11413        assert_eq!(
11414            all_hunks, all_expanded_hunks,
11415            "Editor hunks should not change and all be expanded"
11416        );
11417    });
11418
11419    cx.set_diff_base(Some("new diff base!"));
11420    executor.run_until_parked();
11421
11422    cx.update_editor(|editor, cx| {
11423        let snapshot = editor.snapshot(cx);
11424        let all_hunks = editor_hunks(editor, &snapshot, cx);
11425        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11426        assert_eq!(
11427            expanded_hunks_background_highlights(editor, cx),
11428            Vec::new(),
11429            "After diff base is changed, old git highlights should be removed"
11430        );
11431        assert_eq!(
11432            all_expanded_hunks,
11433            Vec::new(),
11434            "After diff base is changed, old git hunk expansions should be removed"
11435        );
11436        assert_eq!(
11437            all_hunks,
11438            vec![(
11439                "new diff base!".to_string(),
11440                DiffHunkStatus::Modified,
11441                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
11442            )],
11443            "After diff base is changed, hunks should update"
11444        );
11445    });
11446}
11447
11448#[gpui::test]
11449async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11450    init_test(cx, |_| {});
11451
11452    let mut cx = EditorTestContext::new(cx).await;
11453
11454    let diff_base = r#"
11455        use some::mod1;
11456        use some::mod2;
11457
11458        const A: u32 = 42;
11459        const B: u32 = 42;
11460        const C: u32 = 42;
11461
11462        fn main(ˇ) {
11463            println!("hello");
11464
11465            println!("world");
11466        }
11467
11468        fn another() {
11469            println!("another");
11470        }
11471
11472        fn another2() {
11473            println!("another2");
11474        }
11475        "#
11476    .unindent();
11477
11478    cx.set_state(
11479        &r#"
11480        «use some::mod2;
11481
11482        const A: u32 = 42;
11483        const C: u32 = 42;
11484
11485        fn main() {
11486            //println!("hello");
11487
11488            println!("world");
11489            //
11490            //ˇ»
11491        }
11492
11493        fn another() {
11494            println!("another");
11495            println!("another");
11496        }
11497
11498            println!("another2");
11499        }
11500        "#
11501        .unindent(),
11502    );
11503
11504    cx.set_diff_base(Some(&diff_base));
11505    executor.run_until_parked();
11506    cx.update_editor(|editor, cx| {
11507        let snapshot = editor.snapshot(cx);
11508        let all_hunks = editor_hunks(editor, &snapshot, cx);
11509        assert_eq!(
11510            all_hunks,
11511            vec![
11512                (
11513                    "use some::mod1;\n".to_string(),
11514                    DiffHunkStatus::Removed,
11515                    DisplayRow(0)..DisplayRow(0)
11516                ),
11517                (
11518                    "const B: u32 = 42;\n".to_string(),
11519                    DiffHunkStatus::Removed,
11520                    DisplayRow(3)..DisplayRow(3)
11521                ),
11522                (
11523                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11524                    DiffHunkStatus::Modified,
11525                    DisplayRow(5)..DisplayRow(7)
11526                ),
11527                (
11528                    "".to_string(),
11529                    DiffHunkStatus::Added,
11530                    DisplayRow(9)..DisplayRow(11)
11531                ),
11532                (
11533                    "".to_string(),
11534                    DiffHunkStatus::Added,
11535                    DisplayRow(15)..DisplayRow(16)
11536                ),
11537                (
11538                    "fn another2() {\n".to_string(),
11539                    DiffHunkStatus::Removed,
11540                    DisplayRow(18)..DisplayRow(18)
11541                ),
11542            ]
11543        );
11544    });
11545
11546    cx.update_editor(|editor, cx| {
11547        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11548    });
11549    executor.run_until_parked();
11550    cx.assert_editor_state(
11551        &r#"
11552        «use some::mod2;
11553
11554        const A: u32 = 42;
11555        const C: u32 = 42;
11556
11557        fn main() {
11558            //println!("hello");
11559
11560            println!("world");
11561            //
11562            //ˇ»
11563        }
11564
11565        fn another() {
11566            println!("another");
11567            println!("another");
11568        }
11569
11570            println!("another2");
11571        }
11572        "#
11573        .unindent(),
11574    );
11575    cx.update_editor(|editor, cx| {
11576        let snapshot = editor.snapshot(cx);
11577        let all_hunks = editor_hunks(editor, &snapshot, cx);
11578        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11579        assert_eq!(
11580            expanded_hunks_background_highlights(editor, cx),
11581            vec![
11582                DisplayRow(9)..=DisplayRow(10),
11583                DisplayRow(13)..=DisplayRow(14),
11584                DisplayRow(19)..=DisplayRow(19)
11585            ]
11586        );
11587        assert_eq!(
11588            all_hunks,
11589            vec![
11590                (
11591                    "use some::mod1;\n".to_string(),
11592                    DiffHunkStatus::Removed,
11593                    DisplayRow(1)..DisplayRow(1)
11594                ),
11595                (
11596                    "const B: u32 = 42;\n".to_string(),
11597                    DiffHunkStatus::Removed,
11598                    DisplayRow(5)..DisplayRow(5)
11599                ),
11600                (
11601                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11602                    DiffHunkStatus::Modified,
11603                    DisplayRow(9)..DisplayRow(11)
11604                ),
11605                (
11606                    "".to_string(),
11607                    DiffHunkStatus::Added,
11608                    DisplayRow(13)..DisplayRow(15)
11609                ),
11610                (
11611                    "".to_string(),
11612                    DiffHunkStatus::Added,
11613                    DisplayRow(19)..DisplayRow(20)
11614                ),
11615                (
11616                    "fn another2() {\n".to_string(),
11617                    DiffHunkStatus::Removed,
11618                    DisplayRow(23)..DisplayRow(23)
11619                ),
11620            ],
11621        );
11622        assert_eq!(all_hunks, all_expanded_hunks);
11623    });
11624
11625    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11626    cx.executor().run_until_parked();
11627    cx.assert_editor_state(
11628        &r#"
11629        «use some::mod2;
11630
11631        const A: u32 = 42;
11632        const C: u32 = 42;
11633
11634        fn main() {
11635            //println!("hello");
11636
11637            println!("world");
11638            //
11639            //ˇ»
11640        }
11641
11642        fn another() {
11643            println!("another");
11644            println!("another");
11645        }
11646
11647            println!("another2");
11648        }
11649        "#
11650        .unindent(),
11651    );
11652    cx.update_editor(|editor, cx| {
11653        let snapshot = editor.snapshot(cx);
11654        let all_hunks = editor_hunks(editor, &snapshot, cx);
11655        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11656        assert_eq!(
11657            expanded_hunks_background_highlights(editor, cx),
11658            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11659            "Only one hunk is left not folded, its highlight should be visible"
11660        );
11661        assert_eq!(
11662            all_hunks,
11663            vec![
11664                (
11665                    "use some::mod1;\n".to_string(),
11666                    DiffHunkStatus::Removed,
11667                    DisplayRow(0)..DisplayRow(0)
11668                ),
11669                (
11670                    "const B: u32 = 42;\n".to_string(),
11671                    DiffHunkStatus::Removed,
11672                    DisplayRow(0)..DisplayRow(0)
11673                ),
11674                (
11675                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11676                    DiffHunkStatus::Modified,
11677                    DisplayRow(0)..DisplayRow(0)
11678                ),
11679                (
11680                    "".to_string(),
11681                    DiffHunkStatus::Added,
11682                    DisplayRow(0)..DisplayRow(1)
11683                ),
11684                (
11685                    "".to_string(),
11686                    DiffHunkStatus::Added,
11687                    DisplayRow(5)..DisplayRow(6)
11688                ),
11689                (
11690                    "fn another2() {\n".to_string(),
11691                    DiffHunkStatus::Removed,
11692                    DisplayRow(9)..DisplayRow(9)
11693                ),
11694            ],
11695            "Hunk list should still return shifted folded hunks"
11696        );
11697        assert_eq!(
11698            all_expanded_hunks,
11699            vec![
11700                (
11701                    "".to_string(),
11702                    DiffHunkStatus::Added,
11703                    DisplayRow(5)..DisplayRow(6)
11704                ),
11705                (
11706                    "fn another2() {\n".to_string(),
11707                    DiffHunkStatus::Removed,
11708                    DisplayRow(9)..DisplayRow(9)
11709                ),
11710            ],
11711            "Only non-folded hunks should be left expanded"
11712        );
11713    });
11714
11715    cx.update_editor(|editor, cx| {
11716        editor.select_all(&SelectAll, cx);
11717        editor.unfold_lines(&UnfoldLines, cx);
11718    });
11719    cx.executor().run_until_parked();
11720    cx.assert_editor_state(
11721        &r#"
11722        «use some::mod2;
11723
11724        const A: u32 = 42;
11725        const C: u32 = 42;
11726
11727        fn main() {
11728            //println!("hello");
11729
11730            println!("world");
11731            //
11732            //
11733        }
11734
11735        fn another() {
11736            println!("another");
11737            println!("another");
11738        }
11739
11740            println!("another2");
11741        }
11742        ˇ»"#
11743        .unindent(),
11744    );
11745    cx.update_editor(|editor, cx| {
11746        let snapshot = editor.snapshot(cx);
11747        let all_hunks = editor_hunks(editor, &snapshot, cx);
11748        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11749        assert_eq!(
11750            expanded_hunks_background_highlights(editor, cx),
11751            vec![
11752                DisplayRow(9)..=DisplayRow(10),
11753                DisplayRow(13)..=DisplayRow(14),
11754                DisplayRow(19)..=DisplayRow(19)
11755            ],
11756            "After unfolding, all hunk diffs should be visible again"
11757        );
11758        assert_eq!(
11759            all_hunks,
11760            vec![
11761                (
11762                    "use some::mod1;\n".to_string(),
11763                    DiffHunkStatus::Removed,
11764                    DisplayRow(1)..DisplayRow(1)
11765                ),
11766                (
11767                    "const B: u32 = 42;\n".to_string(),
11768                    DiffHunkStatus::Removed,
11769                    DisplayRow(5)..DisplayRow(5)
11770                ),
11771                (
11772                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11773                    DiffHunkStatus::Modified,
11774                    DisplayRow(9)..DisplayRow(11)
11775                ),
11776                (
11777                    "".to_string(),
11778                    DiffHunkStatus::Added,
11779                    DisplayRow(13)..DisplayRow(15)
11780                ),
11781                (
11782                    "".to_string(),
11783                    DiffHunkStatus::Added,
11784                    DisplayRow(19)..DisplayRow(20)
11785                ),
11786                (
11787                    "fn another2() {\n".to_string(),
11788                    DiffHunkStatus::Removed,
11789                    DisplayRow(23)..DisplayRow(23)
11790                ),
11791            ],
11792        );
11793        assert_eq!(all_hunks, all_expanded_hunks);
11794    });
11795}
11796
11797#[gpui::test]
11798async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11799    init_test(cx, |_| {});
11800
11801    let cols = 4;
11802    let rows = 10;
11803    let sample_text_1 = sample_text(rows, cols, 'a');
11804    assert_eq!(
11805        sample_text_1,
11806        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11807    );
11808    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11809    let sample_text_2 = sample_text(rows, cols, 'l');
11810    assert_eq!(
11811        sample_text_2,
11812        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11813    );
11814    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11815    let sample_text_3 = sample_text(rows, cols, 'v');
11816    assert_eq!(
11817        sample_text_3,
11818        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11819    );
11820    let modified_sample_text_3 =
11821        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11822    let buffer_1 = cx.new_model(|cx| {
11823        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11824        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11825        buffer
11826    });
11827    let buffer_2 = cx.new_model(|cx| {
11828        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11829        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11830        buffer
11831    });
11832    let buffer_3 = cx.new_model(|cx| {
11833        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11834        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11835        buffer
11836    });
11837
11838    let multi_buffer = cx.new_model(|cx| {
11839        let mut multibuffer = MultiBuffer::new(ReadWrite);
11840        multibuffer.push_excerpts(
11841            buffer_1.clone(),
11842            [
11843                ExcerptRange {
11844                    context: Point::new(0, 0)..Point::new(3, 0),
11845                    primary: None,
11846                },
11847                ExcerptRange {
11848                    context: Point::new(5, 0)..Point::new(7, 0),
11849                    primary: None,
11850                },
11851                ExcerptRange {
11852                    context: Point::new(9, 0)..Point::new(10, 4),
11853                    primary: None,
11854                },
11855            ],
11856            cx,
11857        );
11858        multibuffer.push_excerpts(
11859            buffer_2.clone(),
11860            [
11861                ExcerptRange {
11862                    context: Point::new(0, 0)..Point::new(3, 0),
11863                    primary: None,
11864                },
11865                ExcerptRange {
11866                    context: Point::new(5, 0)..Point::new(7, 0),
11867                    primary: None,
11868                },
11869                ExcerptRange {
11870                    context: Point::new(9, 0)..Point::new(10, 4),
11871                    primary: None,
11872                },
11873            ],
11874            cx,
11875        );
11876        multibuffer.push_excerpts(
11877            buffer_3.clone(),
11878            [
11879                ExcerptRange {
11880                    context: Point::new(0, 0)..Point::new(3, 0),
11881                    primary: None,
11882                },
11883                ExcerptRange {
11884                    context: Point::new(5, 0)..Point::new(7, 0),
11885                    primary: None,
11886                },
11887                ExcerptRange {
11888                    context: Point::new(9, 0)..Point::new(10, 4),
11889                    primary: None,
11890                },
11891            ],
11892            cx,
11893        );
11894        multibuffer
11895    });
11896
11897    let fs = FakeFs::new(cx.executor());
11898    fs.insert_tree(
11899        "/a",
11900        json!({
11901            "main.rs": modified_sample_text_1,
11902            "other.rs": modified_sample_text_2,
11903            "lib.rs": modified_sample_text_3,
11904        }),
11905    )
11906    .await;
11907
11908    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11909    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11910    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11911    let multi_buffer_editor = cx.new_view(|cx| {
11912        Editor::new(
11913            EditorMode::Full,
11914            multi_buffer,
11915            Some(project.clone()),
11916            true,
11917            cx,
11918        )
11919    });
11920    cx.executor().run_until_parked();
11921
11922    let expected_all_hunks = vec![
11923        (
11924            "bbbb\n".to_string(),
11925            DiffHunkStatus::Removed,
11926            DisplayRow(4)..DisplayRow(4),
11927        ),
11928        (
11929            "nnnn\n".to_string(),
11930            DiffHunkStatus::Modified,
11931            DisplayRow(21)..DisplayRow(22),
11932        ),
11933        (
11934            "".to_string(),
11935            DiffHunkStatus::Added,
11936            DisplayRow(41)..DisplayRow(42),
11937        ),
11938    ];
11939    let expected_all_hunks_shifted = vec![
11940        (
11941            "bbbb\n".to_string(),
11942            DiffHunkStatus::Removed,
11943            DisplayRow(5)..DisplayRow(5),
11944        ),
11945        (
11946            "nnnn\n".to_string(),
11947            DiffHunkStatus::Modified,
11948            DisplayRow(23)..DisplayRow(24),
11949        ),
11950        (
11951            "".to_string(),
11952            DiffHunkStatus::Added,
11953            DisplayRow(43)..DisplayRow(44),
11954        ),
11955    ];
11956
11957    multi_buffer_editor.update(cx, |editor, cx| {
11958        let snapshot = editor.snapshot(cx);
11959        let all_hunks = editor_hunks(editor, &snapshot, cx);
11960        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11961        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11962        assert_eq!(all_hunks, expected_all_hunks);
11963        assert_eq!(all_expanded_hunks, Vec::new());
11964    });
11965
11966    multi_buffer_editor.update(cx, |editor, cx| {
11967        editor.select_all(&SelectAll, cx);
11968        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11969    });
11970    cx.executor().run_until_parked();
11971    multi_buffer_editor.update(cx, |editor, cx| {
11972        let snapshot = editor.snapshot(cx);
11973        let all_hunks = editor_hunks(editor, &snapshot, cx);
11974        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11975        assert_eq!(
11976            expanded_hunks_background_highlights(editor, cx),
11977            vec![
11978                DisplayRow(23)..=DisplayRow(23),
11979                DisplayRow(43)..=DisplayRow(43)
11980            ],
11981        );
11982        assert_eq!(all_hunks, expected_all_hunks_shifted);
11983        assert_eq!(all_hunks, all_expanded_hunks);
11984    });
11985
11986    multi_buffer_editor.update(cx, |editor, cx| {
11987        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11988    });
11989    cx.executor().run_until_parked();
11990    multi_buffer_editor.update(cx, |editor, cx| {
11991        let snapshot = editor.snapshot(cx);
11992        let all_hunks = editor_hunks(editor, &snapshot, cx);
11993        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11994        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11995        assert_eq!(all_hunks, expected_all_hunks);
11996        assert_eq!(all_expanded_hunks, Vec::new());
11997    });
11998
11999    multi_buffer_editor.update(cx, |editor, cx| {
12000        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12001    });
12002    cx.executor().run_until_parked();
12003    multi_buffer_editor.update(cx, |editor, cx| {
12004        let snapshot = editor.snapshot(cx);
12005        let all_hunks = editor_hunks(editor, &snapshot, cx);
12006        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12007        assert_eq!(
12008            expanded_hunks_background_highlights(editor, cx),
12009            vec![
12010                DisplayRow(23)..=DisplayRow(23),
12011                DisplayRow(43)..=DisplayRow(43)
12012            ],
12013        );
12014        assert_eq!(all_hunks, expected_all_hunks_shifted);
12015        assert_eq!(all_hunks, all_expanded_hunks);
12016    });
12017
12018    multi_buffer_editor.update(cx, |editor, cx| {
12019        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12020    });
12021    cx.executor().run_until_parked();
12022    multi_buffer_editor.update(cx, |editor, cx| {
12023        let snapshot = editor.snapshot(cx);
12024        let all_hunks = editor_hunks(editor, &snapshot, cx);
12025        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12026        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12027        assert_eq!(all_hunks, expected_all_hunks);
12028        assert_eq!(all_expanded_hunks, Vec::new());
12029    });
12030}
12031
12032#[gpui::test]
12033async fn test_edits_around_toggled_additions(
12034    executor: BackgroundExecutor,
12035    cx: &mut gpui::TestAppContext,
12036) {
12037    init_test(cx, |_| {});
12038
12039    let mut cx = EditorTestContext::new(cx).await;
12040
12041    let diff_base = r#"
12042        use some::mod1;
12043        use some::mod2;
12044
12045        const A: u32 = 42;
12046
12047        fn main() {
12048            println!("hello");
12049
12050            println!("world");
12051        }
12052        "#
12053    .unindent();
12054    executor.run_until_parked();
12055    cx.set_state(
12056        &r#"
12057        use some::mod1;
12058        use some::mod2;
12059
12060        const A: u32 = 42;
12061        const B: u32 = 42;
12062        const C: u32 = 42;
12063        ˇ
12064
12065        fn main() {
12066            println!("hello");
12067
12068            println!("world");
12069        }
12070        "#
12071        .unindent(),
12072    );
12073
12074    cx.set_diff_base(Some(&diff_base));
12075    executor.run_until_parked();
12076    cx.update_editor(|editor, cx| {
12077        let snapshot = editor.snapshot(cx);
12078        let all_hunks = editor_hunks(editor, &snapshot, cx);
12079        assert_eq!(
12080            all_hunks,
12081            vec![(
12082                "".to_string(),
12083                DiffHunkStatus::Added,
12084                DisplayRow(4)..DisplayRow(7)
12085            )]
12086        );
12087    });
12088    cx.update_editor(|editor, cx| {
12089        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12090    });
12091    executor.run_until_parked();
12092    cx.assert_editor_state(
12093        &r#"
12094        use some::mod1;
12095        use some::mod2;
12096
12097        const A: u32 = 42;
12098        const B: u32 = 42;
12099        const C: u32 = 42;
12100        ˇ
12101
12102        fn main() {
12103            println!("hello");
12104
12105            println!("world");
12106        }
12107        "#
12108        .unindent(),
12109    );
12110    cx.update_editor(|editor, cx| {
12111        let snapshot = editor.snapshot(cx);
12112        let all_hunks = editor_hunks(editor, &snapshot, cx);
12113        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12114        assert_eq!(
12115            all_hunks,
12116            vec![(
12117                "".to_string(),
12118                DiffHunkStatus::Added,
12119                DisplayRow(4)..DisplayRow(7)
12120            )]
12121        );
12122        assert_eq!(
12123            expanded_hunks_background_highlights(editor, cx),
12124            vec![DisplayRow(4)..=DisplayRow(6)]
12125        );
12126        assert_eq!(all_hunks, all_expanded_hunks);
12127    });
12128
12129    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12130    executor.run_until_parked();
12131    cx.assert_editor_state(
12132        &r#"
12133        use some::mod1;
12134        use some::mod2;
12135
12136        const A: u32 = 42;
12137        const B: u32 = 42;
12138        const C: u32 = 42;
12139        const D: u32 = 42;
12140        ˇ
12141
12142        fn main() {
12143            println!("hello");
12144
12145            println!("world");
12146        }
12147        "#
12148        .unindent(),
12149    );
12150    cx.update_editor(|editor, cx| {
12151        let snapshot = editor.snapshot(cx);
12152        let all_hunks = editor_hunks(editor, &snapshot, cx);
12153        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12154        assert_eq!(
12155            all_hunks,
12156            vec![(
12157                "".to_string(),
12158                DiffHunkStatus::Added,
12159                DisplayRow(4)..DisplayRow(8)
12160            )]
12161        );
12162        assert_eq!(
12163            expanded_hunks_background_highlights(editor, cx),
12164            vec![DisplayRow(4)..=DisplayRow(6)],
12165            "Edited hunk should have one more line added"
12166        );
12167        assert_eq!(
12168            all_hunks, all_expanded_hunks,
12169            "Expanded hunk should also grow with the addition"
12170        );
12171    });
12172
12173    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12174    executor.run_until_parked();
12175    cx.assert_editor_state(
12176        &r#"
12177        use some::mod1;
12178        use some::mod2;
12179
12180        const A: u32 = 42;
12181        const B: u32 = 42;
12182        const C: u32 = 42;
12183        const D: u32 = 42;
12184        const E: u32 = 42;
12185        ˇ
12186
12187        fn main() {
12188            println!("hello");
12189
12190            println!("world");
12191        }
12192        "#
12193        .unindent(),
12194    );
12195    cx.update_editor(|editor, cx| {
12196        let snapshot = editor.snapshot(cx);
12197        let all_hunks = editor_hunks(editor, &snapshot, cx);
12198        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12199        assert_eq!(
12200            all_hunks,
12201            vec![(
12202                "".to_string(),
12203                DiffHunkStatus::Added,
12204                DisplayRow(4)..DisplayRow(9)
12205            )]
12206        );
12207        assert_eq!(
12208            expanded_hunks_background_highlights(editor, cx),
12209            vec![DisplayRow(4)..=DisplayRow(6)],
12210            "Edited hunk should have one more line added"
12211        );
12212        assert_eq!(all_hunks, all_expanded_hunks);
12213    });
12214
12215    cx.update_editor(|editor, cx| {
12216        editor.move_up(&MoveUp, cx);
12217        editor.delete_line(&DeleteLine, cx);
12218    });
12219    executor.run_until_parked();
12220    cx.assert_editor_state(
12221        &r#"
12222        use some::mod1;
12223        use some::mod2;
12224
12225        const A: u32 = 42;
12226        const B: u32 = 42;
12227        const C: u32 = 42;
12228        const D: u32 = 42;
12229        ˇ
12230
12231        fn main() {
12232            println!("hello");
12233
12234            println!("world");
12235        }
12236        "#
12237        .unindent(),
12238    );
12239    cx.update_editor(|editor, cx| {
12240        let snapshot = editor.snapshot(cx);
12241        let all_hunks = editor_hunks(editor, &snapshot, cx);
12242        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12243        assert_eq!(
12244            all_hunks,
12245            vec![(
12246                "".to_string(),
12247                DiffHunkStatus::Added,
12248                DisplayRow(4)..DisplayRow(8)
12249            )]
12250        );
12251        assert_eq!(
12252            expanded_hunks_background_highlights(editor, cx),
12253            vec![DisplayRow(4)..=DisplayRow(6)],
12254            "Deleting a line should shrint the hunk"
12255        );
12256        assert_eq!(
12257            all_hunks, all_expanded_hunks,
12258            "Expanded hunk should also shrink with the addition"
12259        );
12260    });
12261
12262    cx.update_editor(|editor, cx| {
12263        editor.move_up(&MoveUp, cx);
12264        editor.delete_line(&DeleteLine, cx);
12265        editor.move_up(&MoveUp, cx);
12266        editor.delete_line(&DeleteLine, cx);
12267        editor.move_up(&MoveUp, cx);
12268        editor.delete_line(&DeleteLine, cx);
12269    });
12270    executor.run_until_parked();
12271    cx.assert_editor_state(
12272        &r#"
12273        use some::mod1;
12274        use some::mod2;
12275
12276        const A: u32 = 42;
12277        ˇ
12278
12279        fn main() {
12280            println!("hello");
12281
12282            println!("world");
12283        }
12284        "#
12285        .unindent(),
12286    );
12287    cx.update_editor(|editor, cx| {
12288        let snapshot = editor.snapshot(cx);
12289        let all_hunks = editor_hunks(editor, &snapshot, cx);
12290        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12291        assert_eq!(
12292            all_hunks,
12293            vec![(
12294                "".to_string(),
12295                DiffHunkStatus::Added,
12296                DisplayRow(5)..DisplayRow(6)
12297            )]
12298        );
12299        assert_eq!(
12300            expanded_hunks_background_highlights(editor, cx),
12301            vec![DisplayRow(5)..=DisplayRow(5)]
12302        );
12303        assert_eq!(all_hunks, all_expanded_hunks);
12304    });
12305
12306    cx.update_editor(|editor, cx| {
12307        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12308        editor.delete_line(&DeleteLine, cx);
12309    });
12310    executor.run_until_parked();
12311    cx.assert_editor_state(
12312        &r#"
12313        ˇ
12314
12315        fn main() {
12316            println!("hello");
12317
12318            println!("world");
12319        }
12320        "#
12321        .unindent(),
12322    );
12323    cx.update_editor(|editor, cx| {
12324        let snapshot = editor.snapshot(cx);
12325        let all_hunks = editor_hunks(editor, &snapshot, cx);
12326        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12327        assert_eq!(
12328            all_hunks,
12329            vec![
12330                (
12331                    "use some::mod1;\nuse some::mod2;\n".to_string(),
12332                    DiffHunkStatus::Removed,
12333                    DisplayRow(0)..DisplayRow(0)
12334                ),
12335                (
12336                    "const A: u32 = 42;\n".to_string(),
12337                    DiffHunkStatus::Removed,
12338                    DisplayRow(2)..DisplayRow(2)
12339                )
12340            ]
12341        );
12342        assert_eq!(
12343            expanded_hunks_background_highlights(editor, cx),
12344            Vec::new(),
12345            "Should close all stale expanded addition hunks"
12346        );
12347        assert_eq!(
12348            all_expanded_hunks,
12349            vec![(
12350                "const A: u32 = 42;\n".to_string(),
12351                DiffHunkStatus::Removed,
12352                DisplayRow(2)..DisplayRow(2)
12353            )],
12354            "Should open hunks that were adjacent to the stale addition one"
12355        );
12356    });
12357}
12358
12359#[gpui::test]
12360async fn test_edits_around_toggled_deletions(
12361    executor: BackgroundExecutor,
12362    cx: &mut gpui::TestAppContext,
12363) {
12364    init_test(cx, |_| {});
12365
12366    let mut cx = EditorTestContext::new(cx).await;
12367
12368    let diff_base = r#"
12369        use some::mod1;
12370        use some::mod2;
12371
12372        const A: u32 = 42;
12373        const B: u32 = 42;
12374        const C: u32 = 42;
12375
12376
12377        fn main() {
12378            println!("hello");
12379
12380            println!("world");
12381        }
12382        "#
12383    .unindent();
12384    executor.run_until_parked();
12385    cx.set_state(
12386        &r#"
12387        use some::mod1;
12388        use some::mod2;
12389
12390        ˇconst B: u32 = 42;
12391        const C: u32 = 42;
12392
12393
12394        fn main() {
12395            println!("hello");
12396
12397            println!("world");
12398        }
12399        "#
12400        .unindent(),
12401    );
12402
12403    cx.set_diff_base(Some(&diff_base));
12404    executor.run_until_parked();
12405    cx.update_editor(|editor, cx| {
12406        let snapshot = editor.snapshot(cx);
12407        let all_hunks = editor_hunks(editor, &snapshot, cx);
12408        assert_eq!(
12409            all_hunks,
12410            vec![(
12411                "const A: u32 = 42;\n".to_string(),
12412                DiffHunkStatus::Removed,
12413                DisplayRow(3)..DisplayRow(3)
12414            )]
12415        );
12416    });
12417    cx.update_editor(|editor, cx| {
12418        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12419    });
12420    executor.run_until_parked();
12421    cx.assert_editor_state(
12422        &r#"
12423        use some::mod1;
12424        use some::mod2;
12425
12426        ˇconst B: u32 = 42;
12427        const C: u32 = 42;
12428
12429
12430        fn main() {
12431            println!("hello");
12432
12433            println!("world");
12434        }
12435        "#
12436        .unindent(),
12437    );
12438    cx.update_editor(|editor, cx| {
12439        let snapshot = editor.snapshot(cx);
12440        let all_hunks = editor_hunks(editor, &snapshot, cx);
12441        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12442        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12443        assert_eq!(
12444            all_hunks,
12445            vec![(
12446                "const A: u32 = 42;\n".to_string(),
12447                DiffHunkStatus::Removed,
12448                DisplayRow(4)..DisplayRow(4)
12449            )]
12450        );
12451        assert_eq!(all_hunks, all_expanded_hunks);
12452    });
12453
12454    cx.update_editor(|editor, cx| {
12455        editor.delete_line(&DeleteLine, cx);
12456    });
12457    executor.run_until_parked();
12458    cx.assert_editor_state(
12459        &r#"
12460        use some::mod1;
12461        use some::mod2;
12462
12463        ˇconst C: u32 = 42;
12464
12465
12466        fn main() {
12467            println!("hello");
12468
12469            println!("world");
12470        }
12471        "#
12472        .unindent(),
12473    );
12474    cx.update_editor(|editor, cx| {
12475        let snapshot = editor.snapshot(cx);
12476        let all_hunks = editor_hunks(editor, &snapshot, cx);
12477        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12478        assert_eq!(
12479            expanded_hunks_background_highlights(editor, cx),
12480            Vec::new(),
12481            "Deleted hunks do not highlight current editor's background"
12482        );
12483        assert_eq!(
12484            all_hunks,
12485            vec![(
12486                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12487                DiffHunkStatus::Removed,
12488                DisplayRow(5)..DisplayRow(5)
12489            )]
12490        );
12491        assert_eq!(all_hunks, all_expanded_hunks);
12492    });
12493
12494    cx.update_editor(|editor, cx| {
12495        editor.delete_line(&DeleteLine, cx);
12496    });
12497    executor.run_until_parked();
12498    cx.assert_editor_state(
12499        &r#"
12500        use some::mod1;
12501        use some::mod2;
12502
12503        ˇ
12504
12505        fn main() {
12506            println!("hello");
12507
12508            println!("world");
12509        }
12510        "#
12511        .unindent(),
12512    );
12513    cx.update_editor(|editor, cx| {
12514        let snapshot = editor.snapshot(cx);
12515        let all_hunks = editor_hunks(editor, &snapshot, cx);
12516        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12517        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12518        assert_eq!(
12519            all_hunks,
12520            vec![(
12521                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12522                DiffHunkStatus::Removed,
12523                DisplayRow(6)..DisplayRow(6)
12524            )]
12525        );
12526        assert_eq!(all_hunks, all_expanded_hunks);
12527    });
12528
12529    cx.update_editor(|editor, cx| {
12530        editor.handle_input("replacement", cx);
12531    });
12532    executor.run_until_parked();
12533    cx.assert_editor_state(
12534        &r#"
12535        use some::mod1;
12536        use some::mod2;
12537
12538        replacementˇ
12539
12540        fn main() {
12541            println!("hello");
12542
12543            println!("world");
12544        }
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            all_hunks,
12554            vec![(
12555                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12556                DiffHunkStatus::Modified,
12557                DisplayRow(7)..DisplayRow(8)
12558            )]
12559        );
12560        assert_eq!(
12561            expanded_hunks_background_highlights(editor, cx),
12562            vec![DisplayRow(7)..=DisplayRow(7)],
12563            "Modified expanded hunks should display additions and highlight their background"
12564        );
12565        assert_eq!(all_hunks, all_expanded_hunks);
12566    });
12567}
12568
12569#[gpui::test]
12570async fn test_edits_around_toggled_modifications(
12571    executor: BackgroundExecutor,
12572    cx: &mut gpui::TestAppContext,
12573) {
12574    init_test(cx, |_| {});
12575
12576    let mut cx = EditorTestContext::new(cx).await;
12577
12578    let diff_base = r#"
12579        use some::mod1;
12580        use some::mod2;
12581
12582        const A: u32 = 42;
12583        const B: u32 = 42;
12584        const C: u32 = 42;
12585        const D: u32 = 42;
12586
12587
12588        fn main() {
12589            println!("hello");
12590
12591            println!("world");
12592        }"#
12593    .unindent();
12594    executor.run_until_parked();
12595    cx.set_state(
12596        &r#"
12597        use some::mod1;
12598        use some::mod2;
12599
12600        const A: u32 = 42;
12601        const B: u32 = 42;
12602        const C: u32 = 43ˇ
12603        const D: u32 = 42;
12604
12605
12606        fn main() {
12607            println!("hello");
12608
12609            println!("world");
12610        }"#
12611        .unindent(),
12612    );
12613
12614    cx.set_diff_base(Some(&diff_base));
12615    executor.run_until_parked();
12616    cx.update_editor(|editor, cx| {
12617        let snapshot = editor.snapshot(cx);
12618        let all_hunks = editor_hunks(editor, &snapshot, cx);
12619        assert_eq!(
12620            all_hunks,
12621            vec![(
12622                "const C: u32 = 42;\n".to_string(),
12623                DiffHunkStatus::Modified,
12624                DisplayRow(5)..DisplayRow(6)
12625            )]
12626        );
12627    });
12628    cx.update_editor(|editor, cx| {
12629        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12630    });
12631    executor.run_until_parked();
12632    cx.assert_editor_state(
12633        &r#"
12634        use some::mod1;
12635        use some::mod2;
12636
12637        const A: u32 = 42;
12638        const B: u32 = 42;
12639        const C: u32 = 43ˇ
12640        const D: u32 = 42;
12641
12642
12643        fn main() {
12644            println!("hello");
12645
12646            println!("world");
12647        }"#
12648        .unindent(),
12649    );
12650    cx.update_editor(|editor, cx| {
12651        let snapshot = editor.snapshot(cx);
12652        let all_hunks = editor_hunks(editor, &snapshot, cx);
12653        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12654        assert_eq!(
12655            expanded_hunks_background_highlights(editor, cx),
12656            vec![DisplayRow(6)..=DisplayRow(6)],
12657        );
12658        assert_eq!(
12659            all_hunks,
12660            vec![(
12661                "const C: u32 = 42;\n".to_string(),
12662                DiffHunkStatus::Modified,
12663                DisplayRow(6)..DisplayRow(7)
12664            )]
12665        );
12666        assert_eq!(all_hunks, all_expanded_hunks);
12667    });
12668
12669    cx.update_editor(|editor, cx| {
12670        editor.handle_input("\nnew_line\n", cx);
12671    });
12672    executor.run_until_parked();
12673    cx.assert_editor_state(
12674        &r#"
12675            use some::mod1;
12676            use some::mod2;
12677
12678            const A: u32 = 42;
12679            const B: u32 = 42;
12680            const C: u32 = 43
12681            new_line
12682            ˇ
12683            const D: u32 = 42;
12684
12685
12686            fn main() {
12687                println!("hello");
12688
12689                println!("world");
12690            }"#
12691        .unindent(),
12692    );
12693    cx.update_editor(|editor, cx| {
12694        let snapshot = editor.snapshot(cx);
12695        let all_hunks = editor_hunks(editor, &snapshot, cx);
12696        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12697        assert_eq!(
12698            expanded_hunks_background_highlights(editor, cx),
12699            vec![DisplayRow(6)..=DisplayRow(6)],
12700            "Modified hunk should grow highlighted lines on more text additions"
12701        );
12702        assert_eq!(
12703            all_hunks,
12704            vec![(
12705                "const C: u32 = 42;\n".to_string(),
12706                DiffHunkStatus::Modified,
12707                DisplayRow(6)..DisplayRow(9)
12708            )]
12709        );
12710        assert_eq!(all_hunks, all_expanded_hunks);
12711    });
12712
12713    cx.update_editor(|editor, cx| {
12714        editor.move_up(&MoveUp, cx);
12715        editor.move_up(&MoveUp, cx);
12716        editor.move_up(&MoveUp, cx);
12717        editor.delete_line(&DeleteLine, cx);
12718    });
12719    executor.run_until_parked();
12720    cx.assert_editor_state(
12721        &r#"
12722            use some::mod1;
12723            use some::mod2;
12724
12725            const A: u32 = 42;
12726            ˇconst C: u32 = 43
12727            new_line
12728
12729            const D: u32 = 42;
12730
12731
12732            fn main() {
12733                println!("hello");
12734
12735                println!("world");
12736            }"#
12737        .unindent(),
12738    );
12739    cx.update_editor(|editor, cx| {
12740        let snapshot = editor.snapshot(cx);
12741        let all_hunks = editor_hunks(editor, &snapshot, cx);
12742        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12743        assert_eq!(
12744            expanded_hunks_background_highlights(editor, cx),
12745            vec![DisplayRow(6)..=DisplayRow(8)],
12746        );
12747        assert_eq!(
12748            all_hunks,
12749            vec![(
12750                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12751                DiffHunkStatus::Modified,
12752                DisplayRow(6)..DisplayRow(9)
12753            )],
12754            "Modified hunk should grow deleted lines on text deletions above"
12755        );
12756        assert_eq!(all_hunks, all_expanded_hunks);
12757    });
12758
12759    cx.update_editor(|editor, cx| {
12760        editor.move_up(&MoveUp, cx);
12761        editor.handle_input("v", cx);
12762    });
12763    executor.run_until_parked();
12764    cx.assert_editor_state(
12765        &r#"
12766            use some::mod1;
12767            use some::mod2;
12768
12769            vˇconst A: u32 = 42;
12770            const C: u32 = 43
12771            new_line
12772
12773            const D: u32 = 42;
12774
12775
12776            fn main() {
12777                println!("hello");
12778
12779                println!("world");
12780            }"#
12781        .unindent(),
12782    );
12783    cx.update_editor(|editor, cx| {
12784        let snapshot = editor.snapshot(cx);
12785        let all_hunks = editor_hunks(editor, &snapshot, cx);
12786        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12787        assert_eq!(
12788            expanded_hunks_background_highlights(editor, cx),
12789            vec![DisplayRow(6)..=DisplayRow(9)],
12790            "Modified hunk should grow deleted lines on text modifications above"
12791        );
12792        assert_eq!(
12793            all_hunks,
12794            vec![(
12795                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12796                DiffHunkStatus::Modified,
12797                DisplayRow(6)..DisplayRow(10)
12798            )]
12799        );
12800        assert_eq!(all_hunks, all_expanded_hunks);
12801    });
12802
12803    cx.update_editor(|editor, cx| {
12804        editor.move_down(&MoveDown, cx);
12805        editor.move_down(&MoveDown, cx);
12806        editor.delete_line(&DeleteLine, cx)
12807    });
12808    executor.run_until_parked();
12809    cx.assert_editor_state(
12810        &r#"
12811            use some::mod1;
12812            use some::mod2;
12813
12814            vconst A: u32 = 42;
12815            const C: u32 = 43
12816            ˇ
12817            const D: u32 = 42;
12818
12819
12820            fn main() {
12821                println!("hello");
12822
12823                println!("world");
12824            }"#
12825        .unindent(),
12826    );
12827    cx.update_editor(|editor, cx| {
12828        let snapshot = editor.snapshot(cx);
12829        let all_hunks = editor_hunks(editor, &snapshot, cx);
12830        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12831        assert_eq!(
12832            expanded_hunks_background_highlights(editor, cx),
12833            vec![DisplayRow(6)..=DisplayRow(8)],
12834            "Modified hunk should grow shrink lines on modification lines removal"
12835        );
12836        assert_eq!(
12837            all_hunks,
12838            vec![(
12839                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12840                DiffHunkStatus::Modified,
12841                DisplayRow(6)..DisplayRow(9)
12842            )]
12843        );
12844        assert_eq!(all_hunks, all_expanded_hunks);
12845    });
12846
12847    cx.update_editor(|editor, cx| {
12848        editor.move_up(&MoveUp, cx);
12849        editor.move_up(&MoveUp, cx);
12850        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12851        editor.delete_line(&DeleteLine, cx)
12852    });
12853    executor.run_until_parked();
12854    cx.assert_editor_state(
12855        &r#"
12856            use some::mod1;
12857            use some::mod2;
12858
12859            ˇ
12860
12861            fn main() {
12862                println!("hello");
12863
12864                println!("world");
12865            }"#
12866        .unindent(),
12867    );
12868    cx.update_editor(|editor, cx| {
12869        let snapshot = editor.snapshot(cx);
12870        let all_hunks = editor_hunks(editor, &snapshot, cx);
12871        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12872        assert_eq!(
12873            expanded_hunks_background_highlights(editor, cx),
12874            Vec::new(),
12875            "Modified hunk should turn into a removed one on all modified lines removal"
12876        );
12877        assert_eq!(
12878            all_hunks,
12879            vec![(
12880                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12881                    .to_string(),
12882                DiffHunkStatus::Removed,
12883                DisplayRow(7)..DisplayRow(7)
12884            )]
12885        );
12886        assert_eq!(all_hunks, all_expanded_hunks);
12887    });
12888}
12889
12890#[gpui::test]
12891async fn test_multiple_expanded_hunks_merge(
12892    executor: BackgroundExecutor,
12893    cx: &mut gpui::TestAppContext,
12894) {
12895    init_test(cx, |_| {});
12896
12897    let mut cx = EditorTestContext::new(cx).await;
12898
12899    let diff_base = r#"
12900        use some::mod1;
12901        use some::mod2;
12902
12903        const A: u32 = 42;
12904        const B: u32 = 42;
12905        const C: u32 = 42;
12906        const D: u32 = 42;
12907
12908
12909        fn main() {
12910            println!("hello");
12911
12912            println!("world");
12913        }"#
12914    .unindent();
12915    executor.run_until_parked();
12916    cx.set_state(
12917        &r#"
12918        use some::mod1;
12919        use some::mod2;
12920
12921        const A: u32 = 42;
12922        const B: u32 = 42;
12923        const C: u32 = 43ˇ
12924        const D: u32 = 42;
12925
12926
12927        fn main() {
12928            println!("hello");
12929
12930            println!("world");
12931        }"#
12932        .unindent(),
12933    );
12934
12935    cx.set_diff_base(Some(&diff_base));
12936    executor.run_until_parked();
12937    cx.update_editor(|editor, cx| {
12938        let snapshot = editor.snapshot(cx);
12939        let all_hunks = editor_hunks(editor, &snapshot, cx);
12940        assert_eq!(
12941            all_hunks,
12942            vec![(
12943                "const C: u32 = 42;\n".to_string(),
12944                DiffHunkStatus::Modified,
12945                DisplayRow(5)..DisplayRow(6)
12946            )]
12947        );
12948    });
12949    cx.update_editor(|editor, cx| {
12950        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12951    });
12952    executor.run_until_parked();
12953    cx.assert_editor_state(
12954        &r#"
12955        use some::mod1;
12956        use some::mod2;
12957
12958        const A: u32 = 42;
12959        const B: u32 = 42;
12960        const C: u32 = 43ˇ
12961        const D: u32 = 42;
12962
12963
12964        fn main() {
12965            println!("hello");
12966
12967            println!("world");
12968        }"#
12969        .unindent(),
12970    );
12971    cx.update_editor(|editor, cx| {
12972        let snapshot = editor.snapshot(cx);
12973        let all_hunks = editor_hunks(editor, &snapshot, cx);
12974        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12975        assert_eq!(
12976            expanded_hunks_background_highlights(editor, cx),
12977            vec![DisplayRow(6)..=DisplayRow(6)],
12978        );
12979        assert_eq!(
12980            all_hunks,
12981            vec![(
12982                "const C: u32 = 42;\n".to_string(),
12983                DiffHunkStatus::Modified,
12984                DisplayRow(6)..DisplayRow(7)
12985            )]
12986        );
12987        assert_eq!(all_hunks, all_expanded_hunks);
12988    });
12989
12990    cx.update_editor(|editor, cx| {
12991        editor.handle_input("\nnew_line\n", cx);
12992    });
12993    executor.run_until_parked();
12994    cx.assert_editor_state(
12995        &r#"
12996            use some::mod1;
12997            use some::mod2;
12998
12999            const A: u32 = 42;
13000            const B: u32 = 42;
13001            const C: u32 = 43
13002            new_line
13003            ˇ
13004            const D: u32 = 42;
13005
13006
13007            fn main() {
13008                println!("hello");
13009
13010                println!("world");
13011            }"#
13012        .unindent(),
13013    );
13014}
13015
13016async fn setup_indent_guides_editor(
13017    text: &str,
13018    cx: &mut gpui::TestAppContext,
13019) -> (BufferId, EditorTestContext) {
13020    init_test(cx, |_| {});
13021
13022    let mut cx = EditorTestContext::new(cx).await;
13023
13024    let buffer_id = cx.update_editor(|editor, cx| {
13025        editor.set_text(text, cx);
13026        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13027
13028        buffer_ids[0]
13029    });
13030
13031    (buffer_id, cx)
13032}
13033
13034fn assert_indent_guides(
13035    range: Range<u32>,
13036    expected: Vec<IndentGuide>,
13037    active_indices: Option<Vec<usize>>,
13038    cx: &mut EditorTestContext,
13039) {
13040    let indent_guides = cx.update_editor(|editor, cx| {
13041        let snapshot = editor.snapshot(cx).display_snapshot;
13042        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13043            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13044            true,
13045            &snapshot,
13046            cx,
13047        );
13048
13049        indent_guides.sort_by(|a, b| {
13050            a.depth.cmp(&b.depth).then(
13051                a.start_row
13052                    .cmp(&b.start_row)
13053                    .then(a.end_row.cmp(&b.end_row)),
13054            )
13055        });
13056        indent_guides
13057    });
13058
13059    if let Some(expected) = active_indices {
13060        let active_indices = cx.update_editor(|editor, cx| {
13061            let snapshot = editor.snapshot(cx).display_snapshot;
13062            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13063        });
13064
13065        assert_eq!(
13066            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13067            expected,
13068            "Active indent guide indices do not match"
13069        );
13070    }
13071
13072    let expected: Vec<_> = expected
13073        .into_iter()
13074        .map(|guide| MultiBufferIndentGuide {
13075            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13076            buffer: guide,
13077        })
13078        .collect();
13079
13080    assert_eq!(indent_guides, expected, "Indent guides do not match");
13081}
13082
13083fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13084    IndentGuide {
13085        buffer_id,
13086        start_row,
13087        end_row,
13088        depth,
13089        tab_size: 4,
13090        settings: IndentGuideSettings {
13091            enabled: true,
13092            line_width: 1,
13093            active_line_width: 1,
13094            ..Default::default()
13095        },
13096    }
13097}
13098
13099#[gpui::test]
13100async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13101    let (buffer_id, mut cx) = setup_indent_guides_editor(
13102        &"
13103    fn main() {
13104        let a = 1;
13105    }"
13106        .unindent(),
13107        cx,
13108    )
13109    .await;
13110
13111    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13112}
13113
13114#[gpui::test]
13115async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13116    let (buffer_id, mut cx) = setup_indent_guides_editor(
13117        &"
13118    fn main() {
13119        let a = 1;
13120        let b = 2;
13121    }"
13122        .unindent(),
13123        cx,
13124    )
13125    .await;
13126
13127    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13128}
13129
13130#[gpui::test]
13131async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13132    let (buffer_id, mut cx) = setup_indent_guides_editor(
13133        &"
13134    fn main() {
13135        let a = 1;
13136        if a == 3 {
13137            let b = 2;
13138        } else {
13139            let c = 3;
13140        }
13141    }"
13142        .unindent(),
13143        cx,
13144    )
13145    .await;
13146
13147    assert_indent_guides(
13148        0..8,
13149        vec![
13150            indent_guide(buffer_id, 1, 6, 0),
13151            indent_guide(buffer_id, 3, 3, 1),
13152            indent_guide(buffer_id, 5, 5, 1),
13153        ],
13154        None,
13155        &mut cx,
13156    );
13157}
13158
13159#[gpui::test]
13160async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13161    let (buffer_id, mut cx) = setup_indent_guides_editor(
13162        &"
13163    fn main() {
13164        let a = 1;
13165            let b = 2;
13166        let c = 3;
13167    }"
13168        .unindent(),
13169        cx,
13170    )
13171    .await;
13172
13173    assert_indent_guides(
13174        0..5,
13175        vec![
13176            indent_guide(buffer_id, 1, 3, 0),
13177            indent_guide(buffer_id, 2, 2, 1),
13178        ],
13179        None,
13180        &mut cx,
13181    );
13182}
13183
13184#[gpui::test]
13185async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13186    let (buffer_id, mut cx) = setup_indent_guides_editor(
13187        &"
13188        fn main() {
13189            let a = 1;
13190
13191            let c = 3;
13192        }"
13193        .unindent(),
13194        cx,
13195    )
13196    .await;
13197
13198    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13199}
13200
13201#[gpui::test]
13202async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13203    let (buffer_id, mut cx) = setup_indent_guides_editor(
13204        &"
13205        fn main() {
13206            let a = 1;
13207
13208            let c = 3;
13209
13210            if a == 3 {
13211                let b = 2;
13212            } else {
13213                let c = 3;
13214            }
13215        }"
13216        .unindent(),
13217        cx,
13218    )
13219    .await;
13220
13221    assert_indent_guides(
13222        0..11,
13223        vec![
13224            indent_guide(buffer_id, 1, 9, 0),
13225            indent_guide(buffer_id, 6, 6, 1),
13226            indent_guide(buffer_id, 8, 8, 1),
13227        ],
13228        None,
13229        &mut cx,
13230    );
13231}
13232
13233#[gpui::test]
13234async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13235    let (buffer_id, mut cx) = setup_indent_guides_editor(
13236        &"
13237        fn main() {
13238            let a = 1;
13239
13240            let c = 3;
13241
13242            if a == 3 {
13243                let b = 2;
13244            } else {
13245                let c = 3;
13246            }
13247        }"
13248        .unindent(),
13249        cx,
13250    )
13251    .await;
13252
13253    assert_indent_guides(
13254        1..11,
13255        vec![
13256            indent_guide(buffer_id, 1, 9, 0),
13257            indent_guide(buffer_id, 6, 6, 1),
13258            indent_guide(buffer_id, 8, 8, 1),
13259        ],
13260        None,
13261        &mut cx,
13262    );
13263}
13264
13265#[gpui::test]
13266async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13267    let (buffer_id, mut cx) = setup_indent_guides_editor(
13268        &"
13269        fn main() {
13270            let a = 1;
13271
13272            let c = 3;
13273
13274            if a == 3 {
13275                let b = 2;
13276            } else {
13277                let c = 3;
13278            }
13279        }"
13280        .unindent(),
13281        cx,
13282    )
13283    .await;
13284
13285    assert_indent_guides(
13286        1..10,
13287        vec![
13288            indent_guide(buffer_id, 1, 9, 0),
13289            indent_guide(buffer_id, 6, 6, 1),
13290            indent_guide(buffer_id, 8, 8, 1),
13291        ],
13292        None,
13293        &mut cx,
13294    );
13295}
13296
13297#[gpui::test]
13298async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13299    let (buffer_id, mut cx) = setup_indent_guides_editor(
13300        &"
13301        block1
13302            block2
13303                block3
13304                    block4
13305            block2
13306        block1
13307        block1"
13308            .unindent(),
13309        cx,
13310    )
13311    .await;
13312
13313    assert_indent_guides(
13314        1..10,
13315        vec![
13316            indent_guide(buffer_id, 1, 4, 0),
13317            indent_guide(buffer_id, 2, 3, 1),
13318            indent_guide(buffer_id, 3, 3, 2),
13319        ],
13320        None,
13321        &mut cx,
13322    );
13323}
13324
13325#[gpui::test]
13326async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13327    let (buffer_id, mut cx) = setup_indent_guides_editor(
13328        &"
13329        block1
13330            block2
13331                block3
13332
13333        block1
13334        block1"
13335            .unindent(),
13336        cx,
13337    )
13338    .await;
13339
13340    assert_indent_guides(
13341        0..6,
13342        vec![
13343            indent_guide(buffer_id, 1, 2, 0),
13344            indent_guide(buffer_id, 2, 2, 1),
13345        ],
13346        None,
13347        &mut cx,
13348    );
13349}
13350
13351#[gpui::test]
13352async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13353    let (buffer_id, mut cx) = setup_indent_guides_editor(
13354        &"
13355        block1
13356
13357
13358
13359            block2
13360        "
13361        .unindent(),
13362        cx,
13363    )
13364    .await;
13365
13366    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13367}
13368
13369#[gpui::test]
13370async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13371    let (buffer_id, mut cx) = setup_indent_guides_editor(
13372        &"
13373        def a:
13374        \tb = 3
13375        \tif True:
13376        \t\tc = 4
13377        \t\td = 5
13378        \tprint(b)
13379        "
13380        .unindent(),
13381        cx,
13382    )
13383    .await;
13384
13385    assert_indent_guides(
13386        0..6,
13387        vec![
13388            indent_guide(buffer_id, 1, 6, 0),
13389            indent_guide(buffer_id, 3, 4, 1),
13390        ],
13391        None,
13392        &mut cx,
13393    );
13394}
13395
13396#[gpui::test]
13397async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13398    let (buffer_id, mut cx) = setup_indent_guides_editor(
13399        &"
13400    fn main() {
13401        let a = 1;
13402    }"
13403        .unindent(),
13404        cx,
13405    )
13406    .await;
13407
13408    cx.update_editor(|editor, cx| {
13409        editor.change_selections(None, cx, |s| {
13410            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13411        });
13412    });
13413
13414    assert_indent_guides(
13415        0..3,
13416        vec![indent_guide(buffer_id, 1, 1, 0)],
13417        Some(vec![0]),
13418        &mut cx,
13419    );
13420}
13421
13422#[gpui::test]
13423async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13424    let (buffer_id, mut cx) = setup_indent_guides_editor(
13425        &"
13426    fn main() {
13427        if 1 == 2 {
13428            let a = 1;
13429        }
13430    }"
13431        .unindent(),
13432        cx,
13433    )
13434    .await;
13435
13436    cx.update_editor(|editor, cx| {
13437        editor.change_selections(None, cx, |s| {
13438            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13439        });
13440    });
13441
13442    assert_indent_guides(
13443        0..4,
13444        vec![
13445            indent_guide(buffer_id, 1, 3, 0),
13446            indent_guide(buffer_id, 2, 2, 1),
13447        ],
13448        Some(vec![1]),
13449        &mut cx,
13450    );
13451
13452    cx.update_editor(|editor, cx| {
13453        editor.change_selections(None, cx, |s| {
13454            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13455        });
13456    });
13457
13458    assert_indent_guides(
13459        0..4,
13460        vec![
13461            indent_guide(buffer_id, 1, 3, 0),
13462            indent_guide(buffer_id, 2, 2, 1),
13463        ],
13464        Some(vec![1]),
13465        &mut cx,
13466    );
13467
13468    cx.update_editor(|editor, cx| {
13469        editor.change_selections(None, cx, |s| {
13470            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13471        });
13472    });
13473
13474    assert_indent_guides(
13475        0..4,
13476        vec![
13477            indent_guide(buffer_id, 1, 3, 0),
13478            indent_guide(buffer_id, 2, 2, 1),
13479        ],
13480        Some(vec![0]),
13481        &mut cx,
13482    );
13483}
13484
13485#[gpui::test]
13486async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13487    let (buffer_id, mut cx) = setup_indent_guides_editor(
13488        &"
13489    fn main() {
13490        let a = 1;
13491
13492        let b = 2;
13493    }"
13494        .unindent(),
13495        cx,
13496    )
13497    .await;
13498
13499    cx.update_editor(|editor, cx| {
13500        editor.change_selections(None, cx, |s| {
13501            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13502        });
13503    });
13504
13505    assert_indent_guides(
13506        0..5,
13507        vec![indent_guide(buffer_id, 1, 3, 0)],
13508        Some(vec![0]),
13509        &mut cx,
13510    );
13511}
13512
13513#[gpui::test]
13514async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13515    let (buffer_id, mut cx) = setup_indent_guides_editor(
13516        &"
13517    def m:
13518        a = 1
13519        pass"
13520            .unindent(),
13521        cx,
13522    )
13523    .await;
13524
13525    cx.update_editor(|editor, cx| {
13526        editor.change_selections(None, cx, |s| {
13527            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13528        });
13529    });
13530
13531    assert_indent_guides(
13532        0..3,
13533        vec![indent_guide(buffer_id, 1, 2, 0)],
13534        Some(vec![0]),
13535        &mut cx,
13536    );
13537}
13538
13539#[gpui::test]
13540fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13541    init_test(cx, |_| {});
13542
13543    let editor = cx.add_window(|cx| {
13544        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13545        build_editor(buffer, cx)
13546    });
13547
13548    let render_args = Arc::new(Mutex::new(None));
13549    let snapshot = editor
13550        .update(cx, |editor, cx| {
13551            let snapshot = editor.buffer().read(cx).snapshot(cx);
13552            let range =
13553                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13554
13555            struct RenderArgs {
13556                row: MultiBufferRow,
13557                folded: bool,
13558                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13559            }
13560
13561            let crease = Crease::new(
13562                range,
13563                FoldPlaceholder::test(),
13564                {
13565                    let toggle_callback = render_args.clone();
13566                    move |row, folded, callback, _cx| {
13567                        *toggle_callback.lock() = Some(RenderArgs {
13568                            row,
13569                            folded,
13570                            callback,
13571                        });
13572                        div()
13573                    }
13574                },
13575                |_row, _folded, _cx| div(),
13576            );
13577
13578            editor.insert_creases(Some(crease), cx);
13579            let snapshot = editor.snapshot(cx);
13580            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13581            snapshot
13582        })
13583        .unwrap();
13584
13585    let render_args = render_args.lock().take().unwrap();
13586    assert_eq!(render_args.row, MultiBufferRow(1));
13587    assert!(!render_args.folded);
13588    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13589
13590    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13591        .unwrap();
13592    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13593    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13594
13595    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13596        .unwrap();
13597    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13598    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13599}
13600
13601#[gpui::test]
13602async fn test_input_text(cx: &mut gpui::TestAppContext) {
13603    init_test(cx, |_| {});
13604    let mut cx = EditorTestContext::new(cx).await;
13605
13606    cx.set_state(
13607        &r#"ˇone
13608        two
13609
13610        three
13611        fourˇ
13612        five
13613
13614        siˇx"#
13615            .unindent(),
13616    );
13617
13618    cx.dispatch_action(HandleInput(String::new()));
13619    cx.assert_editor_state(
13620        &r#"ˇone
13621        two
13622
13623        three
13624        fourˇ
13625        five
13626
13627        siˇx"#
13628            .unindent(),
13629    );
13630
13631    cx.dispatch_action(HandleInput("AAAA".to_string()));
13632    cx.assert_editor_state(
13633        &r#"AAAAˇone
13634        two
13635
13636        three
13637        fourAAAAˇ
13638        five
13639
13640        siAAAAˇx"#
13641            .unindent(),
13642    );
13643}
13644
13645#[gpui::test]
13646async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13647    init_test(cx, |_| {});
13648
13649    let mut cx = EditorTestContext::new(cx).await;
13650    cx.set_state(
13651        r#"let foo = 1;
13652let foo = 2;
13653let foo = 3;
13654let fooˇ = 4;
13655let foo = 5;
13656let foo = 6;
13657let foo = 7;
13658let foo = 8;
13659let foo = 9;
13660let foo = 10;
13661let foo = 11;
13662let foo = 12;
13663let foo = 13;
13664let foo = 14;
13665let foo = 15;"#,
13666    );
13667
13668    cx.update_editor(|e, cx| {
13669        assert_eq!(
13670            e.next_scroll_position,
13671            NextScrollCursorCenterTopBottom::Center,
13672            "Default next scroll direction is center",
13673        );
13674
13675        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13676        assert_eq!(
13677            e.next_scroll_position,
13678            NextScrollCursorCenterTopBottom::Top,
13679            "After center, next scroll direction should be top",
13680        );
13681
13682        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13683        assert_eq!(
13684            e.next_scroll_position,
13685            NextScrollCursorCenterTopBottom::Bottom,
13686            "After top, next scroll direction should be bottom",
13687        );
13688
13689        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13690        assert_eq!(
13691            e.next_scroll_position,
13692            NextScrollCursorCenterTopBottom::Center,
13693            "After bottom, scrolling should start over",
13694        );
13695
13696        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13697        assert_eq!(
13698            e.next_scroll_position,
13699            NextScrollCursorCenterTopBottom::Top,
13700            "Scrolling continues if retriggered fast enough"
13701        );
13702    });
13703
13704    cx.executor()
13705        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13706    cx.executor().run_until_parked();
13707    cx.update_editor(|e, _| {
13708        assert_eq!(
13709            e.next_scroll_position,
13710            NextScrollCursorCenterTopBottom::Center,
13711            "If scrolling is not triggered fast enough, it should reset"
13712        );
13713    });
13714}
13715
13716#[gpui::test]
13717async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13718    init_test(cx, |_| {});
13719    let mut cx = EditorLspTestContext::new_rust(
13720        lsp::ServerCapabilities {
13721            definition_provider: Some(lsp::OneOf::Left(true)),
13722            references_provider: Some(lsp::OneOf::Left(true)),
13723            ..lsp::ServerCapabilities::default()
13724        },
13725        cx,
13726    )
13727    .await;
13728
13729    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13730        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13731            move |params, _| async move {
13732                if empty_go_to_definition {
13733                    Ok(None)
13734                } else {
13735                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13736                        uri: params.text_document_position_params.text_document.uri,
13737                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13738                    })))
13739                }
13740            },
13741        );
13742        let references =
13743            cx.lsp
13744                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13745                    Ok(Some(vec![lsp::Location {
13746                        uri: params.text_document_position.text_document.uri,
13747                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13748                    }]))
13749                });
13750        (go_to_definition, references)
13751    };
13752
13753    cx.set_state(
13754        &r#"fn one() {
13755            let mut a = ˇtwo();
13756        }
13757
13758        fn two() {}"#
13759            .unindent(),
13760    );
13761    set_up_lsp_handlers(false, &mut cx);
13762    let navigated = cx
13763        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13764        .await
13765        .expect("Failed to navigate to definition");
13766    assert_eq!(
13767        navigated,
13768        Navigated::Yes,
13769        "Should have navigated to definition from the GetDefinition response"
13770    );
13771    cx.assert_editor_state(
13772        &r#"fn one() {
13773            let mut a = two();
13774        }
13775
13776        fn «twoˇ»() {}"#
13777            .unindent(),
13778    );
13779
13780    let editors = cx.update_workspace(|workspace, cx| {
13781        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13782    });
13783    cx.update_editor(|_, test_editor_cx| {
13784        assert_eq!(
13785            editors.len(),
13786            1,
13787            "Initially, only one, test, editor should be open in the workspace"
13788        );
13789        assert_eq!(
13790            test_editor_cx.view(),
13791            editors.last().expect("Asserted len is 1")
13792        );
13793    });
13794
13795    set_up_lsp_handlers(true, &mut cx);
13796    let navigated = cx
13797        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13798        .await
13799        .expect("Failed to navigate to lookup references");
13800    assert_eq!(
13801        navigated,
13802        Navigated::Yes,
13803        "Should have navigated to references as a fallback after empty GoToDefinition response"
13804    );
13805    // We should not change the selections in the existing file,
13806    // if opening another milti buffer with the references
13807    cx.assert_editor_state(
13808        &r#"fn one() {
13809            let mut a = two();
13810        }
13811
13812        fn «twoˇ»() {}"#
13813            .unindent(),
13814    );
13815    let editors = cx.update_workspace(|workspace, cx| {
13816        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13817    });
13818    cx.update_editor(|_, test_editor_cx| {
13819        assert_eq!(
13820            editors.len(),
13821            2,
13822            "After falling back to references search, we open a new editor with the results"
13823        );
13824        let references_fallback_text = editors
13825            .into_iter()
13826            .find(|new_editor| new_editor != test_editor_cx.view())
13827            .expect("Should have one non-test editor now")
13828            .read(test_editor_cx)
13829            .text(test_editor_cx);
13830        assert_eq!(
13831            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13832            "Should use the range from the references response and not the GoToDefinition one"
13833        );
13834    });
13835}
13836
13837fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13838    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13839    point..point
13840}
13841
13842fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13843    let (text, ranges) = marked_text_ranges(marked_text, true);
13844    assert_eq!(view.text(cx), text);
13845    assert_eq!(
13846        view.selections.ranges(cx),
13847        ranges,
13848        "Assert selections are {}",
13849        marked_text
13850    );
13851}
13852
13853pub fn handle_signature_help_request(
13854    cx: &mut EditorLspTestContext,
13855    mocked_response: lsp::SignatureHelp,
13856) -> impl Future<Output = ()> {
13857    let mut request =
13858        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13859            let mocked_response = mocked_response.clone();
13860            async move { Ok(Some(mocked_response)) }
13861        });
13862
13863    async move {
13864        request.next().await;
13865    }
13866}
13867
13868/// Handle completion request passing a marked string specifying where the completion
13869/// should be triggered from using '|' character, what range should be replaced, and what completions
13870/// should be returned using '<' and '>' to delimit the range
13871pub fn handle_completion_request(
13872    cx: &mut EditorLspTestContext,
13873    marked_string: &str,
13874    completions: Vec<&'static str>,
13875    counter: Arc<AtomicUsize>,
13876) -> impl Future<Output = ()> {
13877    let complete_from_marker: TextRangeMarker = '|'.into();
13878    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13879    let (_, mut marked_ranges) = marked_text_ranges_by(
13880        marked_string,
13881        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13882    );
13883
13884    let complete_from_position =
13885        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13886    let replace_range =
13887        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13888
13889    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13890        let completions = completions.clone();
13891        counter.fetch_add(1, atomic::Ordering::Release);
13892        async move {
13893            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13894            assert_eq!(
13895                params.text_document_position.position,
13896                complete_from_position
13897            );
13898            Ok(Some(lsp::CompletionResponse::Array(
13899                completions
13900                    .iter()
13901                    .map(|completion_text| lsp::CompletionItem {
13902                        label: completion_text.to_string(),
13903                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13904                            range: replace_range,
13905                            new_text: completion_text.to_string(),
13906                        })),
13907                        ..Default::default()
13908                    })
13909                    .collect(),
13910            )))
13911        }
13912    });
13913
13914    async move {
13915        request.next().await;
13916    }
13917}
13918
13919fn handle_resolve_completion_request(
13920    cx: &mut EditorLspTestContext,
13921    edits: Option<Vec<(&'static str, &'static str)>>,
13922) -> impl Future<Output = ()> {
13923    let edits = edits.map(|edits| {
13924        edits
13925            .iter()
13926            .map(|(marked_string, new_text)| {
13927                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13928                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13929                lsp::TextEdit::new(replace_range, new_text.to_string())
13930            })
13931            .collect::<Vec<_>>()
13932    });
13933
13934    let mut request =
13935        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13936            let edits = edits.clone();
13937            async move {
13938                Ok(lsp::CompletionItem {
13939                    additional_text_edits: edits,
13940                    ..Default::default()
13941                })
13942            }
13943        });
13944
13945    async move {
13946        request.next().await;
13947    }
13948}
13949
13950pub(crate) fn update_test_language_settings(
13951    cx: &mut TestAppContext,
13952    f: impl Fn(&mut AllLanguageSettingsContent),
13953) {
13954    cx.update(|cx| {
13955        SettingsStore::update_global(cx, |store, cx| {
13956            store.update_user_settings::<AllLanguageSettings>(cx, f);
13957        });
13958    });
13959}
13960
13961pub(crate) fn update_test_project_settings(
13962    cx: &mut TestAppContext,
13963    f: impl Fn(&mut ProjectSettings),
13964) {
13965    cx.update(|cx| {
13966        SettingsStore::update_global(cx, |store, cx| {
13967            store.update_user_settings::<ProjectSettings>(cx, f);
13968        });
13969    });
13970}
13971
13972pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13973    cx.update(|cx| {
13974        assets::Assets.load_test_fonts(cx);
13975        let store = SettingsStore::test(cx);
13976        cx.set_global(store);
13977        theme::init(theme::LoadThemes::JustBase, cx);
13978        release_channel::init(SemanticVersion::default(), cx);
13979        client::init_settings(cx);
13980        language::init(cx);
13981        Project::init_settings(cx);
13982        workspace::init_settings(cx);
13983        crate::init(cx);
13984    });
13985
13986    update_test_language_settings(cx, f);
13987}
13988
13989pub(crate) fn rust_lang() -> Arc<Language> {
13990    Arc::new(Language::new(
13991        LanguageConfig {
13992            name: "Rust".into(),
13993            matcher: LanguageMatcher {
13994                path_suffixes: vec!["rs".to_string()],
13995                ..Default::default()
13996            },
13997            ..Default::default()
13998        },
13999        Some(tree_sitter_rust::LANGUAGE.into()),
14000    ))
14001}
14002
14003#[track_caller]
14004fn assert_hunk_revert(
14005    not_reverted_text_with_selections: &str,
14006    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14007    expected_reverted_text_with_selections: &str,
14008    base_text: &str,
14009    cx: &mut EditorLspTestContext,
14010) {
14011    cx.set_state(not_reverted_text_with_selections);
14012    cx.update_editor(|editor, cx| {
14013        editor
14014            .buffer()
14015            .read(cx)
14016            .as_singleton()
14017            .unwrap()
14018            .update(cx, |buffer, cx| {
14019                buffer.set_diff_base(Some(base_text.into()), cx);
14020            });
14021    });
14022    cx.executor().run_until_parked();
14023
14024    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14025        let snapshot = editor.buffer().read(cx).snapshot(cx);
14026        let reverted_hunk_statuses = snapshot
14027            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
14028            .map(|hunk| hunk_status(&hunk))
14029            .collect::<Vec<_>>();
14030
14031        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14032        reverted_hunk_statuses
14033    });
14034    cx.executor().run_until_parked();
14035    cx.assert_editor_state(expected_reverted_text_with_selections);
14036    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14037}