editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   24    LanguageName, Override, ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(
 2106            &DeleteToPreviousWordStart {
 2107                ignore_newlines: false,
 2108            },
 2109            cx,
 2110        );
 2111        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2112    });
 2113
 2114    _ = view.update(cx, |view, cx| {
 2115        view.change_selections(None, cx, |s| {
 2116            s.select_display_ranges([
 2117                // an empty selection - the following word fragment is deleted
 2118                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2119                // characters selected - they are deleted
 2120                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2121            ])
 2122        });
 2123        view.delete_to_next_word_end(
 2124            &DeleteToNextWordEnd {
 2125                ignore_newlines: false,
 2126            },
 2127            cx,
 2128        );
 2129        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2130    });
 2131}
 2132
 2133#[gpui::test]
 2134fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136
 2137    let view = cx.add_window(|cx| {
 2138        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2139        build_editor(buffer.clone(), cx)
 2140    });
 2141    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2142        ignore_newlines: false,
 2143    };
 2144    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2145        ignore_newlines: true,
 2146    };
 2147
 2148    _ = view.update(cx, |view, cx| {
 2149        view.change_selections(None, cx, |s| {
 2150            s.select_display_ranges([
 2151                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2152            ])
 2153        });
 2154        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2155        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2156        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2157        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2158        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2159        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2160        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2161        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2162        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2163        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2164        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2165        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2166    });
 2167}
 2168
 2169#[gpui::test]
 2170fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2171    init_test(cx, |_| {});
 2172
 2173    let view = cx.add_window(|cx| {
 2174        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2175        build_editor(buffer.clone(), cx)
 2176    });
 2177    let del_to_next_word_end = DeleteToNextWordEnd {
 2178        ignore_newlines: false,
 2179    };
 2180    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2181        ignore_newlines: true,
 2182    };
 2183
 2184    _ = view.update(cx, |view, cx| {
 2185        view.change_selections(None, cx, |s| {
 2186            s.select_display_ranges([
 2187                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2188            ])
 2189        });
 2190        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2191        assert_eq!(
 2192            view.buffer.read(cx).read(cx).text(),
 2193            "one\n   two\nthree\n   four"
 2194        );
 2195        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2196        assert_eq!(
 2197            view.buffer.read(cx).read(cx).text(),
 2198            "\n   two\nthree\n   four"
 2199        );
 2200        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2201        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2202        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2203        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2204        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2205        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2206        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2207        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2208    });
 2209}
 2210
 2211#[gpui::test]
 2212fn test_newline(cx: &mut TestAppContext) {
 2213    init_test(cx, |_| {});
 2214
 2215    let view = cx.add_window(|cx| {
 2216        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2217        build_editor(buffer.clone(), cx)
 2218    });
 2219
 2220    _ = view.update(cx, |view, cx| {
 2221        view.change_selections(None, cx, |s| {
 2222            s.select_display_ranges([
 2223                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2224                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2225                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2226            ])
 2227        });
 2228
 2229        view.newline(&Newline, cx);
 2230        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2231    });
 2232}
 2233
 2234#[gpui::test]
 2235fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2236    init_test(cx, |_| {});
 2237
 2238    let editor = cx.add_window(|cx| {
 2239        let buffer = MultiBuffer::build_simple(
 2240            "
 2241                a
 2242                b(
 2243                    X
 2244                )
 2245                c(
 2246                    X
 2247                )
 2248            "
 2249            .unindent()
 2250            .as_str(),
 2251            cx,
 2252        );
 2253        let mut editor = build_editor(buffer.clone(), cx);
 2254        editor.change_selections(None, cx, |s| {
 2255            s.select_ranges([
 2256                Point::new(2, 4)..Point::new(2, 5),
 2257                Point::new(5, 4)..Point::new(5, 5),
 2258            ])
 2259        });
 2260        editor
 2261    });
 2262
 2263    _ = editor.update(cx, |editor, cx| {
 2264        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2265        editor.buffer.update(cx, |buffer, cx| {
 2266            buffer.edit(
 2267                [
 2268                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2269                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2270                ],
 2271                None,
 2272                cx,
 2273            );
 2274            assert_eq!(
 2275                buffer.read(cx).text(),
 2276                "
 2277                    a
 2278                    b()
 2279                    c()
 2280                "
 2281                .unindent()
 2282            );
 2283        });
 2284        assert_eq!(
 2285            editor.selections.ranges(cx),
 2286            &[
 2287                Point::new(1, 2)..Point::new(1, 2),
 2288                Point::new(2, 2)..Point::new(2, 2),
 2289            ],
 2290        );
 2291
 2292        editor.newline(&Newline, cx);
 2293        assert_eq!(
 2294            editor.text(cx),
 2295            "
 2296                a
 2297                b(
 2298                )
 2299                c(
 2300                )
 2301            "
 2302            .unindent()
 2303        );
 2304
 2305        // The selections are moved after the inserted newlines
 2306        assert_eq!(
 2307            editor.selections.ranges(cx),
 2308            &[
 2309                Point::new(2, 0)..Point::new(2, 0),
 2310                Point::new(4, 0)..Point::new(4, 0),
 2311            ],
 2312        );
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2318    init_test(cx, |settings| {
 2319        settings.defaults.tab_size = NonZeroU32::new(4)
 2320    });
 2321
 2322    let language = Arc::new(
 2323        Language::new(
 2324            LanguageConfig::default(),
 2325            Some(tree_sitter_rust::LANGUAGE.into()),
 2326        )
 2327        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2328        .unwrap(),
 2329    );
 2330
 2331    let mut cx = EditorTestContext::new(cx).await;
 2332    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2333    cx.set_state(indoc! {"
 2334        const a: ˇA = (
 2335 2336                «const_functionˇ»(ˇ),
 2337                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2338 2339        ˇ);ˇ
 2340    "});
 2341
 2342    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2343    cx.assert_editor_state(indoc! {"
 2344        ˇ
 2345        const a: A = (
 2346            ˇ
 2347            (
 2348                ˇ
 2349                ˇ
 2350                const_function(),
 2351                ˇ
 2352                ˇ
 2353                ˇ
 2354                ˇ
 2355                something_else,
 2356                ˇ
 2357            )
 2358            ˇ
 2359            ˇ
 2360        );
 2361    "});
 2362}
 2363
 2364#[gpui::test]
 2365async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2366    init_test(cx, |settings| {
 2367        settings.defaults.tab_size = NonZeroU32::new(4)
 2368    });
 2369
 2370    let language = Arc::new(
 2371        Language::new(
 2372            LanguageConfig::default(),
 2373            Some(tree_sitter_rust::LANGUAGE.into()),
 2374        )
 2375        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2376        .unwrap(),
 2377    );
 2378
 2379    let mut cx = EditorTestContext::new(cx).await;
 2380    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2381    cx.set_state(indoc! {"
 2382        const a: ˇA = (
 2383 2384                «const_functionˇ»(ˇ),
 2385                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2386 2387        ˇ);ˇ
 2388    "});
 2389
 2390    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2391    cx.assert_editor_state(indoc! {"
 2392        const a: A = (
 2393            ˇ
 2394            (
 2395                ˇ
 2396                const_function(),
 2397                ˇ
 2398                ˇ
 2399                something_else,
 2400                ˇ
 2401                ˇ
 2402                ˇ
 2403                ˇ
 2404            )
 2405            ˇ
 2406        );
 2407        ˇ
 2408        ˇ
 2409    "});
 2410}
 2411
 2412#[gpui::test]
 2413async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2414    init_test(cx, |settings| {
 2415        settings.defaults.tab_size = NonZeroU32::new(4)
 2416    });
 2417
 2418    let language = Arc::new(Language::new(
 2419        LanguageConfig {
 2420            line_comments: vec!["//".into()],
 2421            ..LanguageConfig::default()
 2422        },
 2423        None,
 2424    ));
 2425    {
 2426        let mut cx = EditorTestContext::new(cx).await;
 2427        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2428        cx.set_state(indoc! {"
 2429        // Fooˇ
 2430    "});
 2431
 2432        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2433        cx.assert_editor_state(indoc! {"
 2434        // Foo
 2435        //ˇ
 2436    "});
 2437        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2438        cx.set_state(indoc! {"
 2439        ˇ// Foo
 2440    "});
 2441        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2442        cx.assert_editor_state(indoc! {"
 2443
 2444        ˇ// Foo
 2445    "});
 2446    }
 2447    // Ensure that comment continuations can be disabled.
 2448    update_test_language_settings(cx, |settings| {
 2449        settings.defaults.extend_comment_on_newline = Some(false);
 2450    });
 2451    let mut cx = EditorTestContext::new(cx).await;
 2452    cx.set_state(indoc! {"
 2453        // Fooˇ
 2454    "});
 2455    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2456    cx.assert_editor_state(indoc! {"
 2457        // Foo
 2458        ˇ
 2459    "});
 2460}
 2461
 2462#[gpui::test]
 2463fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2464    init_test(cx, |_| {});
 2465
 2466    let editor = cx.add_window(|cx| {
 2467        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2468        let mut editor = build_editor(buffer.clone(), cx);
 2469        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2470        editor
 2471    });
 2472
 2473    _ = editor.update(cx, |editor, cx| {
 2474        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2475        editor.buffer.update(cx, |buffer, cx| {
 2476            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2477            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2478        });
 2479        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2480
 2481        editor.insert("Z", cx);
 2482        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2483
 2484        // The selections are moved after the inserted characters
 2485        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2486    });
 2487}
 2488
 2489#[gpui::test]
 2490async fn test_tab(cx: &mut gpui::TestAppContext) {
 2491    init_test(cx, |settings| {
 2492        settings.defaults.tab_size = NonZeroU32::new(3)
 2493    });
 2494
 2495    let mut cx = EditorTestContext::new(cx).await;
 2496    cx.set_state(indoc! {"
 2497        ˇabˇc
 2498        ˇ🏀ˇ🏀ˇefg
 2499 2500    "});
 2501    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2502    cx.assert_editor_state(indoc! {"
 2503           ˇab ˇc
 2504           ˇ🏀  ˇ🏀  ˇefg
 2505        d  ˇ
 2506    "});
 2507
 2508    cx.set_state(indoc! {"
 2509        a
 2510        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2511    "});
 2512    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2513    cx.assert_editor_state(indoc! {"
 2514        a
 2515           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2516    "});
 2517}
 2518
 2519#[gpui::test]
 2520async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2521    init_test(cx, |_| {});
 2522
 2523    let mut cx = EditorTestContext::new(cx).await;
 2524    let language = Arc::new(
 2525        Language::new(
 2526            LanguageConfig::default(),
 2527            Some(tree_sitter_rust::LANGUAGE.into()),
 2528        )
 2529        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2530        .unwrap(),
 2531    );
 2532    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533
 2534    // cursors that are already at the suggested indent level insert
 2535    // a soft tab. cursors that are to the left of the suggested indent
 2536    // auto-indent their line.
 2537    cx.set_state(indoc! {"
 2538        ˇ
 2539        const a: B = (
 2540            c(
 2541                d(
 2542        ˇ
 2543                )
 2544        ˇ
 2545        ˇ    )
 2546        );
 2547    "});
 2548    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2549    cx.assert_editor_state(indoc! {"
 2550            ˇ
 2551        const a: B = (
 2552            c(
 2553                d(
 2554                    ˇ
 2555                )
 2556                ˇ
 2557            ˇ)
 2558        );
 2559    "});
 2560
 2561    // handle auto-indent when there are multiple cursors on the same line
 2562    cx.set_state(indoc! {"
 2563        const a: B = (
 2564            c(
 2565        ˇ    ˇ
 2566        ˇ    )
 2567        );
 2568    "});
 2569    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2570    cx.assert_editor_state(indoc! {"
 2571        const a: B = (
 2572            c(
 2573                ˇ
 2574            ˇ)
 2575        );
 2576    "});
 2577}
 2578
 2579#[gpui::test]
 2580async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2581    init_test(cx, |settings| {
 2582        settings.defaults.tab_size = NonZeroU32::new(4)
 2583    });
 2584
 2585    let language = Arc::new(
 2586        Language::new(
 2587            LanguageConfig::default(),
 2588            Some(tree_sitter_rust::LANGUAGE.into()),
 2589        )
 2590        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2591        .unwrap(),
 2592    );
 2593
 2594    let mut cx = EditorTestContext::new(cx).await;
 2595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2596    cx.set_state(indoc! {"
 2597        fn a() {
 2598            if b {
 2599        \t ˇc
 2600            }
 2601        }
 2602    "});
 2603
 2604    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2605    cx.assert_editor_state(indoc! {"
 2606        fn a() {
 2607            if b {
 2608                ˇc
 2609            }
 2610        }
 2611    "});
 2612}
 2613
 2614#[gpui::test]
 2615async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2616    init_test(cx, |settings| {
 2617        settings.defaults.tab_size = NonZeroU32::new(4);
 2618    });
 2619
 2620    let mut cx = EditorTestContext::new(cx).await;
 2621
 2622    cx.set_state(indoc! {"
 2623          «oneˇ» «twoˇ»
 2624        three
 2625         four
 2626    "});
 2627    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2628    cx.assert_editor_state(indoc! {"
 2629            «oneˇ» «twoˇ»
 2630        three
 2631         four
 2632    "});
 2633
 2634    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2635    cx.assert_editor_state(indoc! {"
 2636        «oneˇ» «twoˇ»
 2637        three
 2638         four
 2639    "});
 2640
 2641    // select across line ending
 2642    cx.set_state(indoc! {"
 2643        one two
 2644        t«hree
 2645        ˇ» four
 2646    "});
 2647    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2648    cx.assert_editor_state(indoc! {"
 2649        one two
 2650            t«hree
 2651        ˇ» four
 2652    "});
 2653
 2654    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2655    cx.assert_editor_state(indoc! {"
 2656        one two
 2657        t«hree
 2658        ˇ» four
 2659    "});
 2660
 2661    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2662    cx.set_state(indoc! {"
 2663        one two
 2664        ˇthree
 2665            four
 2666    "});
 2667    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2668    cx.assert_editor_state(indoc! {"
 2669        one two
 2670            ˇthree
 2671            four
 2672    "});
 2673
 2674    cx.set_state(indoc! {"
 2675        one two
 2676        ˇ    three
 2677            four
 2678    "});
 2679    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        one two
 2682        ˇthree
 2683            four
 2684    "});
 2685}
 2686
 2687#[gpui::test]
 2688async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2689    init_test(cx, |settings| {
 2690        settings.defaults.hard_tabs = Some(true);
 2691    });
 2692
 2693    let mut cx = EditorTestContext::new(cx).await;
 2694
 2695    // select two ranges on one line
 2696    cx.set_state(indoc! {"
 2697        «oneˇ» «twoˇ»
 2698        three
 2699        four
 2700    "});
 2701    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2702    cx.assert_editor_state(indoc! {"
 2703        \t«oneˇ» «twoˇ»
 2704        three
 2705        four
 2706    "});
 2707    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2708    cx.assert_editor_state(indoc! {"
 2709        \t\t«oneˇ» «twoˇ»
 2710        three
 2711        four
 2712    "});
 2713    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2714    cx.assert_editor_state(indoc! {"
 2715        \t«oneˇ» «twoˇ»
 2716        three
 2717        four
 2718    "});
 2719    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2720    cx.assert_editor_state(indoc! {"
 2721        «oneˇ» «twoˇ»
 2722        three
 2723        four
 2724    "});
 2725
 2726    // select across a line ending
 2727    cx.set_state(indoc! {"
 2728        one two
 2729        t«hree
 2730        ˇ»four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734        one two
 2735        \tt«hree
 2736        ˇ»four
 2737    "});
 2738    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2739    cx.assert_editor_state(indoc! {"
 2740        one two
 2741        \t\tt«hree
 2742        ˇ»four
 2743    "});
 2744    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2745    cx.assert_editor_state(indoc! {"
 2746        one two
 2747        \tt«hree
 2748        ˇ»four
 2749    "});
 2750    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2751    cx.assert_editor_state(indoc! {"
 2752        one two
 2753        t«hree
 2754        ˇ»four
 2755    "});
 2756
 2757    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2758    cx.set_state(indoc! {"
 2759        one two
 2760        ˇthree
 2761        four
 2762    "});
 2763    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2764    cx.assert_editor_state(indoc! {"
 2765        one two
 2766        ˇthree
 2767        four
 2768    "});
 2769    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2770    cx.assert_editor_state(indoc! {"
 2771        one two
 2772        \tˇthree
 2773        four
 2774    "});
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        one two
 2778        ˇthree
 2779        four
 2780    "});
 2781}
 2782
 2783#[gpui::test]
 2784fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2785    init_test(cx, |settings| {
 2786        settings.languages.extend([
 2787            (
 2788                "TOML".into(),
 2789                LanguageSettingsContent {
 2790                    tab_size: NonZeroU32::new(2),
 2791                    ..Default::default()
 2792                },
 2793            ),
 2794            (
 2795                "Rust".into(),
 2796                LanguageSettingsContent {
 2797                    tab_size: NonZeroU32::new(4),
 2798                    ..Default::default()
 2799                },
 2800            ),
 2801        ]);
 2802    });
 2803
 2804    let toml_language = Arc::new(Language::new(
 2805        LanguageConfig {
 2806            name: "TOML".into(),
 2807            ..Default::default()
 2808        },
 2809        None,
 2810    ));
 2811    let rust_language = Arc::new(Language::new(
 2812        LanguageConfig {
 2813            name: "Rust".into(),
 2814            ..Default::default()
 2815        },
 2816        None,
 2817    ));
 2818
 2819    let toml_buffer =
 2820        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2821    let rust_buffer = cx.new_model(|cx| {
 2822        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2823    });
 2824    let multibuffer = cx.new_model(|cx| {
 2825        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2826        multibuffer.push_excerpts(
 2827            toml_buffer.clone(),
 2828            [ExcerptRange {
 2829                context: Point::new(0, 0)..Point::new(2, 0),
 2830                primary: None,
 2831            }],
 2832            cx,
 2833        );
 2834        multibuffer.push_excerpts(
 2835            rust_buffer.clone(),
 2836            [ExcerptRange {
 2837                context: Point::new(0, 0)..Point::new(1, 0),
 2838                primary: None,
 2839            }],
 2840            cx,
 2841        );
 2842        multibuffer
 2843    });
 2844
 2845    cx.add_window(|cx| {
 2846        let mut editor = build_editor(multibuffer, cx);
 2847
 2848        assert_eq!(
 2849            editor.text(cx),
 2850            indoc! {"
 2851                a = 1
 2852                b = 2
 2853
 2854                const c: usize = 3;
 2855            "}
 2856        );
 2857
 2858        select_ranges(
 2859            &mut editor,
 2860            indoc! {"
 2861                «aˇ» = 1
 2862                b = 2
 2863
 2864                «const c:ˇ» usize = 3;
 2865            "},
 2866            cx,
 2867        );
 2868
 2869        editor.tab(&Tab, cx);
 2870        assert_text_with_selections(
 2871            &mut editor,
 2872            indoc! {"
 2873                  «aˇ» = 1
 2874                b = 2
 2875
 2876                    «const c:ˇ» usize = 3;
 2877            "},
 2878            cx,
 2879        );
 2880        editor.tab_prev(&TabPrev, cx);
 2881        assert_text_with_selections(
 2882            &mut editor,
 2883            indoc! {"
 2884                «aˇ» = 1
 2885                b = 2
 2886
 2887                «const c:ˇ» usize = 3;
 2888            "},
 2889            cx,
 2890        );
 2891
 2892        editor
 2893    });
 2894}
 2895
 2896#[gpui::test]
 2897async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2898    init_test(cx, |_| {});
 2899
 2900    let mut cx = EditorTestContext::new(cx).await;
 2901
 2902    // Basic backspace
 2903    cx.set_state(indoc! {"
 2904        onˇe two three
 2905        fou«rˇ» five six
 2906        seven «ˇeight nine
 2907        »ten
 2908    "});
 2909    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2910    cx.assert_editor_state(indoc! {"
 2911        oˇe two three
 2912        fouˇ five six
 2913        seven ˇten
 2914    "});
 2915
 2916    // Test backspace inside and around indents
 2917    cx.set_state(indoc! {"
 2918        zero
 2919            ˇone
 2920                ˇtwo
 2921            ˇ ˇ ˇ  three
 2922        ˇ  ˇ  four
 2923    "});
 2924    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        zero
 2927        ˇone
 2928            ˇtwo
 2929        ˇ  threeˇ  four
 2930    "});
 2931
 2932    // Test backspace with line_mode set to true
 2933    cx.update_editor(|e, _| e.selections.line_mode = true);
 2934    cx.set_state(indoc! {"
 2935        The ˇquick ˇbrown
 2936        fox jumps over
 2937        the lazy dog
 2938        ˇThe qu«ick bˇ»rown"});
 2939    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        ˇfox jumps over
 2942        the lazy dogˇ"});
 2943}
 2944
 2945#[gpui::test]
 2946async fn test_delete(cx: &mut gpui::TestAppContext) {
 2947    init_test(cx, |_| {});
 2948
 2949    let mut cx = EditorTestContext::new(cx).await;
 2950    cx.set_state(indoc! {"
 2951        onˇe two three
 2952        fou«rˇ» five six
 2953        seven «ˇeight nine
 2954        »ten
 2955    "});
 2956    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2957    cx.assert_editor_state(indoc! {"
 2958        onˇ two three
 2959        fouˇ five six
 2960        seven ˇten
 2961    "});
 2962
 2963    // Test backspace with line_mode set to true
 2964    cx.update_editor(|e, _| e.selections.line_mode = true);
 2965    cx.set_state(indoc! {"
 2966        The ˇquick ˇbrown
 2967        fox «ˇjum»ps over
 2968        the lazy dog
 2969        ˇThe qu«ick bˇ»rown"});
 2970    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2971    cx.assert_editor_state("ˇthe lazy dogˇ");
 2972}
 2973
 2974#[gpui::test]
 2975fn test_delete_line(cx: &mut TestAppContext) {
 2976    init_test(cx, |_| {});
 2977
 2978    let view = cx.add_window(|cx| {
 2979        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2980        build_editor(buffer, cx)
 2981    });
 2982    _ = view.update(cx, |view, cx| {
 2983        view.change_selections(None, cx, |s| {
 2984            s.select_display_ranges([
 2985                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2986                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2987                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2988            ])
 2989        });
 2990        view.delete_line(&DeleteLine, cx);
 2991        assert_eq!(view.display_text(cx), "ghi");
 2992        assert_eq!(
 2993            view.selections.display_ranges(cx),
 2994            vec![
 2995                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2996                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2997            ]
 2998        );
 2999    });
 3000
 3001    let view = cx.add_window(|cx| {
 3002        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3003        build_editor(buffer, cx)
 3004    });
 3005    _ = view.update(cx, |view, cx| {
 3006        view.change_selections(None, cx, |s| {
 3007            s.select_display_ranges([
 3008                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3009            ])
 3010        });
 3011        view.delete_line(&DeleteLine, cx);
 3012        assert_eq!(view.display_text(cx), "ghi\n");
 3013        assert_eq!(
 3014            view.selections.display_ranges(cx),
 3015            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3016        );
 3017    });
 3018}
 3019
 3020#[gpui::test]
 3021fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3022    init_test(cx, |_| {});
 3023
 3024    cx.add_window(|cx| {
 3025        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3026        let mut editor = build_editor(buffer.clone(), cx);
 3027        let buffer = buffer.read(cx).as_singleton().unwrap();
 3028
 3029        assert_eq!(
 3030            editor.selections.ranges::<Point>(cx),
 3031            &[Point::new(0, 0)..Point::new(0, 0)]
 3032        );
 3033
 3034        // When on single line, replace newline at end by space
 3035        editor.join_lines(&JoinLines, cx);
 3036        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3037        assert_eq!(
 3038            editor.selections.ranges::<Point>(cx),
 3039            &[Point::new(0, 3)..Point::new(0, 3)]
 3040        );
 3041
 3042        // When multiple lines are selected, remove newlines that are spanned by the selection
 3043        editor.change_selections(None, cx, |s| {
 3044            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3045        });
 3046        editor.join_lines(&JoinLines, cx);
 3047        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3048        assert_eq!(
 3049            editor.selections.ranges::<Point>(cx),
 3050            &[Point::new(0, 11)..Point::new(0, 11)]
 3051        );
 3052
 3053        // Undo should be transactional
 3054        editor.undo(&Undo, cx);
 3055        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3056        assert_eq!(
 3057            editor.selections.ranges::<Point>(cx),
 3058            &[Point::new(0, 5)..Point::new(2, 2)]
 3059        );
 3060
 3061        // When joining an empty line don't insert a space
 3062        editor.change_selections(None, cx, |s| {
 3063            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3064        });
 3065        editor.join_lines(&JoinLines, cx);
 3066        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3067        assert_eq!(
 3068            editor.selections.ranges::<Point>(cx),
 3069            [Point::new(2, 3)..Point::new(2, 3)]
 3070        );
 3071
 3072        // We can remove trailing newlines
 3073        editor.join_lines(&JoinLines, cx);
 3074        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3075        assert_eq!(
 3076            editor.selections.ranges::<Point>(cx),
 3077            [Point::new(2, 3)..Point::new(2, 3)]
 3078        );
 3079
 3080        // We don't blow up on the last line
 3081        editor.join_lines(&JoinLines, cx);
 3082        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3083        assert_eq!(
 3084            editor.selections.ranges::<Point>(cx),
 3085            [Point::new(2, 3)..Point::new(2, 3)]
 3086        );
 3087
 3088        // reset to test indentation
 3089        editor.buffer.update(cx, |buffer, cx| {
 3090            buffer.edit(
 3091                [
 3092                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3093                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3094                ],
 3095                None,
 3096                cx,
 3097            )
 3098        });
 3099
 3100        // We remove any leading spaces
 3101        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3102        editor.change_selections(None, cx, |s| {
 3103            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3104        });
 3105        editor.join_lines(&JoinLines, cx);
 3106        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3107
 3108        // We don't insert a space for a line containing only spaces
 3109        editor.join_lines(&JoinLines, cx);
 3110        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3111
 3112        // We ignore any leading tabs
 3113        editor.join_lines(&JoinLines, cx);
 3114        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3115
 3116        editor
 3117    });
 3118}
 3119
 3120#[gpui::test]
 3121fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3122    init_test(cx, |_| {});
 3123
 3124    cx.add_window(|cx| {
 3125        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3126        let mut editor = build_editor(buffer.clone(), cx);
 3127        let buffer = buffer.read(cx).as_singleton().unwrap();
 3128
 3129        editor.change_selections(None, cx, |s| {
 3130            s.select_ranges([
 3131                Point::new(0, 2)..Point::new(1, 1),
 3132                Point::new(1, 2)..Point::new(1, 2),
 3133                Point::new(3, 1)..Point::new(3, 2),
 3134            ])
 3135        });
 3136
 3137        editor.join_lines(&JoinLines, cx);
 3138        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3139
 3140        assert_eq!(
 3141            editor.selections.ranges::<Point>(cx),
 3142            [
 3143                Point::new(0, 7)..Point::new(0, 7),
 3144                Point::new(1, 3)..Point::new(1, 3)
 3145            ]
 3146        );
 3147        editor
 3148    });
 3149}
 3150
 3151#[gpui::test]
 3152async fn test_join_lines_with_git_diff_base(
 3153    executor: BackgroundExecutor,
 3154    cx: &mut gpui::TestAppContext,
 3155) {
 3156    init_test(cx, |_| {});
 3157
 3158    let mut cx = EditorTestContext::new(cx).await;
 3159
 3160    let diff_base = r#"
 3161        Line 0
 3162        Line 1
 3163        Line 2
 3164        Line 3
 3165        "#
 3166    .unindent();
 3167
 3168    cx.set_state(
 3169        &r#"
 3170        ˇLine 0
 3171        Line 1
 3172        Line 2
 3173        Line 3
 3174        "#
 3175        .unindent(),
 3176    );
 3177
 3178    cx.set_diff_base(Some(&diff_base));
 3179    executor.run_until_parked();
 3180
 3181    // Join lines
 3182    cx.update_editor(|editor, cx| {
 3183        editor.join_lines(&JoinLines, cx);
 3184    });
 3185    executor.run_until_parked();
 3186
 3187    cx.assert_editor_state(
 3188        &r#"
 3189        Line 0ˇ Line 1
 3190        Line 2
 3191        Line 3
 3192        "#
 3193        .unindent(),
 3194    );
 3195    // Join again
 3196    cx.update_editor(|editor, cx| {
 3197        editor.join_lines(&JoinLines, cx);
 3198    });
 3199    executor.run_until_parked();
 3200
 3201    cx.assert_editor_state(
 3202        &r#"
 3203        Line 0 Line 1ˇ Line 2
 3204        Line 3
 3205        "#
 3206        .unindent(),
 3207    );
 3208}
 3209
 3210#[gpui::test]
 3211async fn test_custom_newlines_cause_no_false_positive_diffs(
 3212    executor: BackgroundExecutor,
 3213    cx: &mut gpui::TestAppContext,
 3214) {
 3215    init_test(cx, |_| {});
 3216    let mut cx = EditorTestContext::new(cx).await;
 3217    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3218    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3219    executor.run_until_parked();
 3220
 3221    cx.update_editor(|editor, cx| {
 3222        assert_eq!(
 3223            editor
 3224                .buffer()
 3225                .read(cx)
 3226                .snapshot(cx)
 3227                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3228                .collect::<Vec<_>>(),
 3229            Vec::new(),
 3230            "Should not have any diffs for files with custom newlines"
 3231        );
 3232    });
 3233}
 3234
 3235#[gpui::test]
 3236async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3237    init_test(cx, |_| {});
 3238
 3239    let mut cx = EditorTestContext::new(cx).await;
 3240
 3241    // Test sort_lines_case_insensitive()
 3242    cx.set_state(indoc! {"
 3243        «z
 3244        y
 3245        x
 3246        Z
 3247        Y
 3248        Xˇ»
 3249    "});
 3250    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        «x
 3253        X
 3254        y
 3255        Y
 3256        z
 3257        Zˇ»
 3258    "});
 3259
 3260    // Test reverse_lines()
 3261    cx.set_state(indoc! {"
 3262        «5
 3263        4
 3264        3
 3265        2
 3266        1ˇ»
 3267    "});
 3268    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3269    cx.assert_editor_state(indoc! {"
 3270        «1
 3271        2
 3272        3
 3273        4
 3274        5ˇ»
 3275    "});
 3276
 3277    // Skip testing shuffle_line()
 3278
 3279    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3280    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3281
 3282    // Don't manipulate when cursor is on single line, but expand the selection
 3283    cx.set_state(indoc! {"
 3284        ddˇdd
 3285        ccc
 3286        bb
 3287        a
 3288    "});
 3289    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3290    cx.assert_editor_state(indoc! {"
 3291        «ddddˇ»
 3292        ccc
 3293        bb
 3294        a
 3295    "});
 3296
 3297    // Basic manipulate case
 3298    // Start selection moves to column 0
 3299    // End of selection shrinks to fit shorter line
 3300    cx.set_state(indoc! {"
 3301        dd«d
 3302        ccc
 3303        bb
 3304        aaaaaˇ»
 3305    "});
 3306    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3307    cx.assert_editor_state(indoc! {"
 3308        «aaaaa
 3309        bb
 3310        ccc
 3311        dddˇ»
 3312    "});
 3313
 3314    // Manipulate case with newlines
 3315    cx.set_state(indoc! {"
 3316        dd«d
 3317        ccc
 3318
 3319        bb
 3320        aaaaa
 3321
 3322        ˇ»
 3323    "});
 3324    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3325    cx.assert_editor_state(indoc! {"
 3326        «
 3327
 3328        aaaaa
 3329        bb
 3330        ccc
 3331        dddˇ»
 3332
 3333    "});
 3334
 3335    // Adding new line
 3336    cx.set_state(indoc! {"
 3337        aa«a
 3338        bbˇ»b
 3339    "});
 3340    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3341    cx.assert_editor_state(indoc! {"
 3342        «aaa
 3343        bbb
 3344        added_lineˇ»
 3345    "});
 3346
 3347    // Removing line
 3348    cx.set_state(indoc! {"
 3349        aa«a
 3350        bbbˇ»
 3351    "});
 3352    cx.update_editor(|e, cx| {
 3353        e.manipulate_lines(cx, |lines| {
 3354            lines.pop();
 3355        })
 3356    });
 3357    cx.assert_editor_state(indoc! {"
 3358        «aaaˇ»
 3359    "});
 3360
 3361    // Removing all lines
 3362    cx.set_state(indoc! {"
 3363        aa«a
 3364        bbbˇ»
 3365    "});
 3366    cx.update_editor(|e, cx| {
 3367        e.manipulate_lines(cx, |lines| {
 3368            lines.drain(..);
 3369        })
 3370    });
 3371    cx.assert_editor_state(indoc! {"
 3372        ˇ
 3373    "});
 3374}
 3375
 3376#[gpui::test]
 3377async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3378    init_test(cx, |_| {});
 3379
 3380    let mut cx = EditorTestContext::new(cx).await;
 3381
 3382    // Consider continuous selection as single selection
 3383    cx.set_state(indoc! {"
 3384        Aaa«aa
 3385        cˇ»c«c
 3386        bb
 3387        aaaˇ»aa
 3388    "});
 3389    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3390    cx.assert_editor_state(indoc! {"
 3391        «Aaaaa
 3392        ccc
 3393        bb
 3394        aaaaaˇ»
 3395    "});
 3396
 3397    cx.set_state(indoc! {"
 3398        Aaa«aa
 3399        cˇ»c«c
 3400        bb
 3401        aaaˇ»aa
 3402    "});
 3403    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3404    cx.assert_editor_state(indoc! {"
 3405        «Aaaaa
 3406        ccc
 3407        bbˇ»
 3408    "});
 3409
 3410    // Consider non continuous selection as distinct dedup operations
 3411    cx.set_state(indoc! {"
 3412        «aaaaa
 3413        bb
 3414        aaaaa
 3415        aaaaaˇ»
 3416
 3417        aaa«aaˇ»
 3418    "});
 3419    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3420    cx.assert_editor_state(indoc! {"
 3421        «aaaaa
 3422        bbˇ»
 3423
 3424        «aaaaaˇ»
 3425    "});
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3430    init_test(cx, |_| {});
 3431
 3432    let mut cx = EditorTestContext::new(cx).await;
 3433
 3434    cx.set_state(indoc! {"
 3435        «Aaa
 3436        aAa
 3437        Aaaˇ»
 3438    "});
 3439    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        «Aaa
 3442        aAaˇ»
 3443    "});
 3444
 3445    cx.set_state(indoc! {"
 3446        «Aaa
 3447        aAa
 3448        aaAˇ»
 3449    "});
 3450    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3451    cx.assert_editor_state(indoc! {"
 3452        «Aaaˇ»
 3453    "});
 3454}
 3455
 3456#[gpui::test]
 3457async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    let mut cx = EditorTestContext::new(cx).await;
 3461
 3462    // Manipulate with multiple selections on a single line
 3463    cx.set_state(indoc! {"
 3464        dd«dd
 3465        cˇ»c«c
 3466        bb
 3467        aaaˇ»aa
 3468    "});
 3469    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3470    cx.assert_editor_state(indoc! {"
 3471        «aaaaa
 3472        bb
 3473        ccc
 3474        ddddˇ»
 3475    "});
 3476
 3477    // Manipulate with multiple disjoin selections
 3478    cx.set_state(indoc! {"
 3479 3480        4
 3481        3
 3482        2
 3483        1ˇ»
 3484
 3485        dd«dd
 3486        ccc
 3487        bb
 3488        aaaˇ»aa
 3489    "});
 3490    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3491    cx.assert_editor_state(indoc! {"
 3492        «1
 3493        2
 3494        3
 3495        4
 3496        5ˇ»
 3497
 3498        «aaaaa
 3499        bb
 3500        ccc
 3501        ddddˇ»
 3502    "});
 3503
 3504    // Adding lines on each selection
 3505    cx.set_state(indoc! {"
 3506 3507        1ˇ»
 3508
 3509        bb«bb
 3510        aaaˇ»aa
 3511    "});
 3512    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3513    cx.assert_editor_state(indoc! {"
 3514        «2
 3515        1
 3516        added lineˇ»
 3517
 3518        «bbbb
 3519        aaaaa
 3520        added lineˇ»
 3521    "});
 3522
 3523    // Removing lines on each selection
 3524    cx.set_state(indoc! {"
 3525 3526        1ˇ»
 3527
 3528        bb«bb
 3529        aaaˇ»aa
 3530    "});
 3531    cx.update_editor(|e, cx| {
 3532        e.manipulate_lines(cx, |lines| {
 3533            lines.pop();
 3534        })
 3535    });
 3536    cx.assert_editor_state(indoc! {"
 3537        «2ˇ»
 3538
 3539        «bbbbˇ»
 3540    "});
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_manipulate_text(cx: &mut TestAppContext) {
 3545    init_test(cx, |_| {});
 3546
 3547    let mut cx = EditorTestContext::new(cx).await;
 3548
 3549    // Test convert_to_upper_case()
 3550    cx.set_state(indoc! {"
 3551        «hello worldˇ»
 3552    "});
 3553    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3554    cx.assert_editor_state(indoc! {"
 3555        «HELLO WORLDˇ»
 3556    "});
 3557
 3558    // Test convert_to_lower_case()
 3559    cx.set_state(indoc! {"
 3560        «HELLO WORLDˇ»
 3561    "});
 3562    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3563    cx.assert_editor_state(indoc! {"
 3564        «hello worldˇ»
 3565    "});
 3566
 3567    // Test multiple line, single selection case
 3568    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3569    cx.set_state(indoc! {"
 3570        «The quick brown
 3571        fox jumps over
 3572        the lazy dogˇ»
 3573    "});
 3574    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «The Quick Brown
 3577        Fox Jumps Over
 3578        The Lazy Dogˇ»
 3579    "});
 3580
 3581    // Test multiple line, single selection case
 3582    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3583    cx.set_state(indoc! {"
 3584        «The quick brown
 3585        fox jumps over
 3586        the lazy dogˇ»
 3587    "});
 3588    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3589    cx.assert_editor_state(indoc! {"
 3590        «TheQuickBrown
 3591        FoxJumpsOver
 3592        TheLazyDogˇ»
 3593    "});
 3594
 3595    // From here on out, test more complex cases of manipulate_text()
 3596
 3597    // Test no selection case - should affect words cursors are in
 3598    // Cursor at beginning, middle, and end of word
 3599    cx.set_state(indoc! {"
 3600        ˇhello big beauˇtiful worldˇ
 3601    "});
 3602    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3605    "});
 3606
 3607    // Test multiple selections on a single line and across multiple lines
 3608    cx.set_state(indoc! {"
 3609        «Theˇ» quick «brown
 3610        foxˇ» jumps «overˇ»
 3611        the «lazyˇ» dog
 3612    "});
 3613    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3614    cx.assert_editor_state(indoc! {"
 3615        «THEˇ» quick «BROWN
 3616        FOXˇ» jumps «OVERˇ»
 3617        the «LAZYˇ» dog
 3618    "});
 3619
 3620    // Test case where text length grows
 3621    cx.set_state(indoc! {"
 3622        «tschüߡ»
 3623    "});
 3624    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3625    cx.assert_editor_state(indoc! {"
 3626        «TSCHÜSSˇ»
 3627    "});
 3628
 3629    // Test to make sure we don't crash when text shrinks
 3630    cx.set_state(indoc! {"
 3631        aaa_bbbˇ
 3632    "});
 3633    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3634    cx.assert_editor_state(indoc! {"
 3635        «aaaBbbˇ»
 3636    "});
 3637
 3638    // Test to make sure we all aware of the fact that each word can grow and shrink
 3639    // Final selections should be aware of this fact
 3640    cx.set_state(indoc! {"
 3641        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3642    "});
 3643    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3644    cx.assert_editor_state(indoc! {"
 3645        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3646    "});
 3647
 3648    cx.set_state(indoc! {"
 3649        «hElLo, WoRld!ˇ»
 3650    "});
 3651    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3652    cx.assert_editor_state(indoc! {"
 3653        «HeLlO, wOrLD!ˇ»
 3654    "});
 3655}
 3656
 3657#[gpui::test]
 3658fn test_duplicate_line(cx: &mut TestAppContext) {
 3659    init_test(cx, |_| {});
 3660
 3661    let view = cx.add_window(|cx| {
 3662        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3663        build_editor(buffer, cx)
 3664    });
 3665    _ = view.update(cx, |view, cx| {
 3666        view.change_selections(None, cx, |s| {
 3667            s.select_display_ranges([
 3668                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3669                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3670                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3671                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3672            ])
 3673        });
 3674        view.duplicate_line_down(&DuplicateLineDown, cx);
 3675        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3676        assert_eq!(
 3677            view.selections.display_ranges(cx),
 3678            vec![
 3679                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3680                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3681                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3682                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3683            ]
 3684        );
 3685    });
 3686
 3687    let view = cx.add_window(|cx| {
 3688        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3689        build_editor(buffer, cx)
 3690    });
 3691    _ = view.update(cx, |view, cx| {
 3692        view.change_selections(None, cx, |s| {
 3693            s.select_display_ranges([
 3694                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3695                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3696            ])
 3697        });
 3698        view.duplicate_line_down(&DuplicateLineDown, cx);
 3699        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3700        assert_eq!(
 3701            view.selections.display_ranges(cx),
 3702            vec![
 3703                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3704                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3705            ]
 3706        );
 3707    });
 3708
 3709    // With `move_upwards` the selections stay in place, except for
 3710    // the lines inserted above them
 3711    let view = cx.add_window(|cx| {
 3712        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3713        build_editor(buffer, cx)
 3714    });
 3715    _ = view.update(cx, |view, cx| {
 3716        view.change_selections(None, cx, |s| {
 3717            s.select_display_ranges([
 3718                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3719                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3720                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3721                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3722            ])
 3723        });
 3724        view.duplicate_line_up(&DuplicateLineUp, cx);
 3725        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3726        assert_eq!(
 3727            view.selections.display_ranges(cx),
 3728            vec![
 3729                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3730                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3731                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3732                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3733            ]
 3734        );
 3735    });
 3736
 3737    let view = cx.add_window(|cx| {
 3738        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3739        build_editor(buffer, cx)
 3740    });
 3741    _ = view.update(cx, |view, cx| {
 3742        view.change_selections(None, cx, |s| {
 3743            s.select_display_ranges([
 3744                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3745                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3746            ])
 3747        });
 3748        view.duplicate_line_up(&DuplicateLineUp, cx);
 3749        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3750        assert_eq!(
 3751            view.selections.display_ranges(cx),
 3752            vec![
 3753                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3754                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3755            ]
 3756        );
 3757    });
 3758}
 3759
 3760#[gpui::test]
 3761fn test_move_line_up_down(cx: &mut TestAppContext) {
 3762    init_test(cx, |_| {});
 3763
 3764    let view = cx.add_window(|cx| {
 3765        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3766        build_editor(buffer, cx)
 3767    });
 3768    _ = view.update(cx, |view, cx| {
 3769        view.fold_ranges(
 3770            vec![
 3771                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3772                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3773                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3774            ],
 3775            true,
 3776            cx,
 3777        );
 3778        view.change_selections(None, cx, |s| {
 3779            s.select_display_ranges([
 3780                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3781                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3782                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3783                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3784            ])
 3785        });
 3786        assert_eq!(
 3787            view.display_text(cx),
 3788            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3789        );
 3790
 3791        view.move_line_up(&MoveLineUp, cx);
 3792        assert_eq!(
 3793            view.display_text(cx),
 3794            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3795        );
 3796        assert_eq!(
 3797            view.selections.display_ranges(cx),
 3798            vec![
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3800                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3801                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3802                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3803            ]
 3804        );
 3805    });
 3806
 3807    _ = view.update(cx, |view, cx| {
 3808        view.move_line_down(&MoveLineDown, cx);
 3809        assert_eq!(
 3810            view.display_text(cx),
 3811            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3812        );
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3818                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3819                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3820            ]
 3821        );
 3822    });
 3823
 3824    _ = view.update(cx, |view, cx| {
 3825        view.move_line_down(&MoveLineDown, cx);
 3826        assert_eq!(
 3827            view.display_text(cx),
 3828            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3829        );
 3830        assert_eq!(
 3831            view.selections.display_ranges(cx),
 3832            vec![
 3833                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3834                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3835                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3836                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3837            ]
 3838        );
 3839    });
 3840
 3841    _ = view.update(cx, |view, cx| {
 3842        view.move_line_up(&MoveLineUp, cx);
 3843        assert_eq!(
 3844            view.display_text(cx),
 3845            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3846        );
 3847        assert_eq!(
 3848            view.selections.display_ranges(cx),
 3849            vec![
 3850                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3851                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3852                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3853                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3854            ]
 3855        );
 3856    });
 3857}
 3858
 3859#[gpui::test]
 3860fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3861    init_test(cx, |_| {});
 3862
 3863    let editor = cx.add_window(|cx| {
 3864        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3865        build_editor(buffer, cx)
 3866    });
 3867    _ = editor.update(cx, |editor, cx| {
 3868        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3869        editor.insert_blocks(
 3870            [BlockProperties {
 3871                style: BlockStyle::Fixed,
 3872                position: snapshot.anchor_after(Point::new(2, 0)),
 3873                disposition: BlockDisposition::Below,
 3874                height: 1,
 3875                render: Box::new(|_| div().into_any()),
 3876                priority: 0,
 3877            }],
 3878            Some(Autoscroll::fit()),
 3879            cx,
 3880        );
 3881        editor.change_selections(None, cx, |s| {
 3882            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3883        });
 3884        editor.move_line_down(&MoveLineDown, cx);
 3885    });
 3886}
 3887
 3888#[gpui::test]
 3889fn test_transpose(cx: &mut TestAppContext) {
 3890    init_test(cx, |_| {});
 3891
 3892    _ = cx.add_window(|cx| {
 3893        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3894        editor.set_style(EditorStyle::default(), cx);
 3895        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3896        editor.transpose(&Default::default(), cx);
 3897        assert_eq!(editor.text(cx), "bac");
 3898        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3899
 3900        editor.transpose(&Default::default(), cx);
 3901        assert_eq!(editor.text(cx), "bca");
 3902        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3903
 3904        editor.transpose(&Default::default(), cx);
 3905        assert_eq!(editor.text(cx), "bac");
 3906        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3907
 3908        editor
 3909    });
 3910
 3911    _ = cx.add_window(|cx| {
 3912        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3913        editor.set_style(EditorStyle::default(), cx);
 3914        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3915        editor.transpose(&Default::default(), cx);
 3916        assert_eq!(editor.text(cx), "acb\nde");
 3917        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3918
 3919        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3920        editor.transpose(&Default::default(), cx);
 3921        assert_eq!(editor.text(cx), "acbd\ne");
 3922        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3923
 3924        editor.transpose(&Default::default(), cx);
 3925        assert_eq!(editor.text(cx), "acbde\n");
 3926        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3927
 3928        editor.transpose(&Default::default(), cx);
 3929        assert_eq!(editor.text(cx), "acbd\ne");
 3930        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3931
 3932        editor
 3933    });
 3934
 3935    _ = cx.add_window(|cx| {
 3936        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3937        editor.set_style(EditorStyle::default(), cx);
 3938        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3939        editor.transpose(&Default::default(), cx);
 3940        assert_eq!(editor.text(cx), "bacd\ne");
 3941        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3942
 3943        editor.transpose(&Default::default(), cx);
 3944        assert_eq!(editor.text(cx), "bcade\n");
 3945        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3946
 3947        editor.transpose(&Default::default(), cx);
 3948        assert_eq!(editor.text(cx), "bcda\ne");
 3949        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3950
 3951        editor.transpose(&Default::default(), cx);
 3952        assert_eq!(editor.text(cx), "bcade\n");
 3953        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3954
 3955        editor.transpose(&Default::default(), cx);
 3956        assert_eq!(editor.text(cx), "bcaed\n");
 3957        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3958
 3959        editor
 3960    });
 3961
 3962    _ = cx.add_window(|cx| {
 3963        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3964        editor.set_style(EditorStyle::default(), cx);
 3965        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3966        editor.transpose(&Default::default(), cx);
 3967        assert_eq!(editor.text(cx), "🏀🍐✋");
 3968        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3969
 3970        editor.transpose(&Default::default(), cx);
 3971        assert_eq!(editor.text(cx), "🏀✋🍐");
 3972        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3973
 3974        editor.transpose(&Default::default(), cx);
 3975        assert_eq!(editor.text(cx), "🏀🍐✋");
 3976        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3977
 3978        editor
 3979    });
 3980}
 3981
 3982#[gpui::test]
 3983async fn test_rewrap(cx: &mut TestAppContext) {
 3984    init_test(cx, |_| {});
 3985
 3986    let mut cx = EditorTestContext::new(cx).await;
 3987
 3988    {
 3989        let language = Arc::new(Language::new(
 3990            LanguageConfig {
 3991                line_comments: vec!["// ".into()],
 3992                ..LanguageConfig::default()
 3993            },
 3994            None,
 3995        ));
 3996        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3997
 3998        let unwrapped_text = indoc! {"
 3999            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4000        "};
 4001
 4002        let wrapped_text = indoc! {"
 4003            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4004            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4005            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4006            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4007            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4008            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4009            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4010            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4011            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4012            // porttitor id. Aliquam id accumsan eros.ˇ
 4013        "};
 4014
 4015        cx.set_state(unwrapped_text);
 4016        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4017        cx.assert_editor_state(wrapped_text);
 4018    }
 4019
 4020    // Test that 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
 4277        let wrapped_text = indoc! {"
 4278            fn foo() {
 4279                if true {
 4280                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4281                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4282                    // egestas tellus id dignissim.ˇ
 4283                    do_something();
 4284                } else {
 4285                    //
 4286                }
 4287
 4288            }
 4289        "};
 4290
 4291        cx.set_state(unwrapped_text);
 4292        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4293        cx.assert_editor_state(wrapped_text);
 4294
 4295        let unwrapped_text = indoc! {"
 4296            fn foo() {
 4297                if true {
 4298            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4299            // Praesent semper egestas tellus id dignissim.»
 4300                    do_something();
 4301                } else {
 4302                    //
 4303                }
 4304
 4305            }
 4306        "};
 4307
 4308        let wrapped_text = indoc! {"
 4309            fn foo() {
 4310                if true {
 4311                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4312                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4313                    // egestas tellus id dignissim.ˇ
 4314                    do_something();
 4315                } else {
 4316                    //
 4317                }
 4318
 4319            }
 4320        "};
 4321
 4322        cx.set_state(unwrapped_text);
 4323        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4324        cx.assert_editor_state(wrapped_text);
 4325    }
 4326}
 4327
 4328#[gpui::test]
 4329async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4330    init_test(cx, |_| {});
 4331
 4332    let mut cx = EditorTestContext::new(cx).await;
 4333
 4334    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4335    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4336    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4337
 4338    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4339    cx.set_state("two ˇfour ˇsix ˇ");
 4340    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4341    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4342
 4343    // Paste again but with only two cursors. Since the number of cursors doesn't
 4344    // match the number of slices in the clipboard, the entire clipboard text
 4345    // is pasted at each cursor.
 4346    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4347    cx.update_editor(|e, cx| {
 4348        e.handle_input("( ", cx);
 4349        e.paste(&Paste, cx);
 4350        e.handle_input(") ", cx);
 4351    });
 4352    cx.assert_editor_state(
 4353        &([
 4354            "( one✅ ",
 4355            "three ",
 4356            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4357            "three ",
 4358            "five ) ˇ",
 4359        ]
 4360        .join("\n")),
 4361    );
 4362
 4363    // Cut with three selections, one of which is full-line.
 4364    cx.set_state(indoc! {"
 4365        1«2ˇ»3
 4366        4ˇ567
 4367        «8ˇ»9"});
 4368    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4369    cx.assert_editor_state(indoc! {"
 4370        1ˇ3
 4371        ˇ9"});
 4372
 4373    // Paste with three selections, noticing how the copied selection that was full-line
 4374    // gets inserted before the second cursor.
 4375    cx.set_state(indoc! {"
 4376        1ˇ3
 4377 4378        «oˇ»ne"});
 4379    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4380    cx.assert_editor_state(indoc! {"
 4381        12ˇ3
 4382        4567
 4383 4384        8ˇne"});
 4385
 4386    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4387    cx.set_state(indoc! {"
 4388        The quick brown
 4389        fox juˇmps over
 4390        the lazy dog"});
 4391    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4392    assert_eq!(
 4393        cx.read_from_clipboard()
 4394            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4395        Some("fox jumps over\n".to_string())
 4396    );
 4397
 4398    // Paste with three selections, noticing how the copied full-line selection is inserted
 4399    // before the empty selections but replaces the selection that is non-empty.
 4400    cx.set_state(indoc! {"
 4401        Tˇhe quick brown
 4402        «foˇ»x jumps over
 4403        tˇhe lazy dog"});
 4404    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4405    cx.assert_editor_state(indoc! {"
 4406        fox jumps over
 4407        Tˇhe quick brown
 4408        fox jumps over
 4409        ˇx jumps over
 4410        fox jumps over
 4411        tˇhe lazy dog"});
 4412}
 4413
 4414#[gpui::test]
 4415async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4416    init_test(cx, |_| {});
 4417
 4418    let mut cx = EditorTestContext::new(cx).await;
 4419    let language = Arc::new(Language::new(
 4420        LanguageConfig::default(),
 4421        Some(tree_sitter_rust::LANGUAGE.into()),
 4422    ));
 4423    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4424
 4425    // Cut an indented block, without the leading whitespace.
 4426    cx.set_state(indoc! {"
 4427        const a: B = (
 4428            c(),
 4429            «d(
 4430                e,
 4431                f
 4432            )ˇ»
 4433        );
 4434    "});
 4435    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4436    cx.assert_editor_state(indoc! {"
 4437        const a: B = (
 4438            c(),
 4439            ˇ
 4440        );
 4441    "});
 4442
 4443    // Paste it at the same position.
 4444    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4445    cx.assert_editor_state(indoc! {"
 4446        const a: B = (
 4447            c(),
 4448            d(
 4449                e,
 4450                f
 4451 4452        );
 4453    "});
 4454
 4455    // Paste it at a line with a lower indent level.
 4456    cx.set_state(indoc! {"
 4457        ˇ
 4458        const a: B = (
 4459            c(),
 4460        );
 4461    "});
 4462    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4463    cx.assert_editor_state(indoc! {"
 4464        d(
 4465            e,
 4466            f
 4467 4468        const a: B = (
 4469            c(),
 4470        );
 4471    "});
 4472
 4473    // Cut an indented block, with the leading whitespace.
 4474    cx.set_state(indoc! {"
 4475        const a: B = (
 4476            c(),
 4477        «    d(
 4478                e,
 4479                f
 4480            )
 4481        ˇ»);
 4482    "});
 4483    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4484    cx.assert_editor_state(indoc! {"
 4485        const a: B = (
 4486            c(),
 4487        ˇ);
 4488    "});
 4489
 4490    // Paste it at the same position.
 4491    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4492    cx.assert_editor_state(indoc! {"
 4493        const a: B = (
 4494            c(),
 4495            d(
 4496                e,
 4497                f
 4498            )
 4499        ˇ);
 4500    "});
 4501
 4502    // Paste it at a line with a higher indent level.
 4503    cx.set_state(indoc! {"
 4504        const a: B = (
 4505            c(),
 4506            d(
 4507                e,
 4508 4509            )
 4510        );
 4511    "});
 4512    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4513    cx.assert_editor_state(indoc! {"
 4514        const a: B = (
 4515            c(),
 4516            d(
 4517                e,
 4518                f    d(
 4519                    e,
 4520                    f
 4521                )
 4522        ˇ
 4523            )
 4524        );
 4525    "});
 4526}
 4527
 4528#[gpui::test]
 4529fn test_select_all(cx: &mut TestAppContext) {
 4530    init_test(cx, |_| {});
 4531
 4532    let view = cx.add_window(|cx| {
 4533        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4534        build_editor(buffer, cx)
 4535    });
 4536    _ = view.update(cx, |view, cx| {
 4537        view.select_all(&SelectAll, cx);
 4538        assert_eq!(
 4539            view.selections.display_ranges(cx),
 4540            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4541        );
 4542    });
 4543}
 4544
 4545#[gpui::test]
 4546fn test_select_line(cx: &mut TestAppContext) {
 4547    init_test(cx, |_| {});
 4548
 4549    let view = cx.add_window(|cx| {
 4550        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4551        build_editor(buffer, cx)
 4552    });
 4553    _ = view.update(cx, |view, cx| {
 4554        view.change_selections(None, cx, |s| {
 4555            s.select_display_ranges([
 4556                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4557                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4558                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4559                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4560            ])
 4561        });
 4562        view.select_line(&SelectLine, cx);
 4563        assert_eq!(
 4564            view.selections.display_ranges(cx),
 4565            vec![
 4566                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4567                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4568            ]
 4569        );
 4570    });
 4571
 4572    _ = view.update(cx, |view, cx| {
 4573        view.select_line(&SelectLine, cx);
 4574        assert_eq!(
 4575            view.selections.display_ranges(cx),
 4576            vec![
 4577                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4578                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4579            ]
 4580        );
 4581    });
 4582
 4583    _ = view.update(cx, |view, cx| {
 4584        view.select_line(&SelectLine, cx);
 4585        assert_eq!(
 4586            view.selections.display_ranges(cx),
 4587            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4588        );
 4589    });
 4590}
 4591
 4592#[gpui::test]
 4593fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4594    init_test(cx, |_| {});
 4595
 4596    let view = cx.add_window(|cx| {
 4597        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4598        build_editor(buffer, cx)
 4599    });
 4600    _ = view.update(cx, |view, cx| {
 4601        view.fold_ranges(
 4602            vec![
 4603                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4604                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4605                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4606            ],
 4607            true,
 4608            cx,
 4609        );
 4610        view.change_selections(None, cx, |s| {
 4611            s.select_display_ranges([
 4612                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4613                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4614                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4615                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4616            ])
 4617        });
 4618        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4619    });
 4620
 4621    _ = view.update(cx, |view, cx| {
 4622        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4623        assert_eq!(
 4624            view.display_text(cx),
 4625            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4626        );
 4627        assert_eq!(
 4628            view.selections.display_ranges(cx),
 4629            [
 4630                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4632                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4633                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4634            ]
 4635        );
 4636    });
 4637
 4638    _ = view.update(cx, |view, cx| {
 4639        view.change_selections(None, cx, |s| {
 4640            s.select_display_ranges([
 4641                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4642            ])
 4643        });
 4644        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4645        assert_eq!(
 4646            view.display_text(cx),
 4647            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4648        );
 4649        assert_eq!(
 4650            view.selections.display_ranges(cx),
 4651            [
 4652                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4653                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4654                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4655                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4656                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4657                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4658                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4659                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4660            ]
 4661        );
 4662    });
 4663}
 4664
 4665#[gpui::test]
 4666async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4667    init_test(cx, |_| {});
 4668
 4669    let mut cx = EditorTestContext::new(cx).await;
 4670
 4671    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4672    cx.set_state(indoc!(
 4673        r#"abc
 4674           defˇghi
 4675
 4676           jk
 4677           nlmo
 4678           "#
 4679    ));
 4680
 4681    cx.update_editor(|editor, cx| {
 4682        editor.add_selection_above(&Default::default(), cx);
 4683    });
 4684
 4685    cx.assert_editor_state(indoc!(
 4686        r#"abcˇ
 4687           defˇghi
 4688
 4689           jk
 4690           nlmo
 4691           "#
 4692    ));
 4693
 4694    cx.update_editor(|editor, cx| {
 4695        editor.add_selection_above(&Default::default(), cx);
 4696    });
 4697
 4698    cx.assert_editor_state(indoc!(
 4699        r#"abcˇ
 4700            defˇghi
 4701
 4702            jk
 4703            nlmo
 4704            "#
 4705    ));
 4706
 4707    cx.update_editor(|view, cx| {
 4708        view.add_selection_below(&Default::default(), cx);
 4709    });
 4710
 4711    cx.assert_editor_state(indoc!(
 4712        r#"abc
 4713           defˇghi
 4714
 4715           jk
 4716           nlmo
 4717           "#
 4718    ));
 4719
 4720    cx.update_editor(|view, cx| {
 4721        view.undo_selection(&Default::default(), cx);
 4722    });
 4723
 4724    cx.assert_editor_state(indoc!(
 4725        r#"abcˇ
 4726           defˇghi
 4727
 4728           jk
 4729           nlmo
 4730           "#
 4731    ));
 4732
 4733    cx.update_editor(|view, cx| {
 4734        view.redo_selection(&Default::default(), cx);
 4735    });
 4736
 4737    cx.assert_editor_state(indoc!(
 4738        r#"abc
 4739           defˇghi
 4740
 4741           jk
 4742           nlmo
 4743           "#
 4744    ));
 4745
 4746    cx.update_editor(|view, cx| {
 4747        view.add_selection_below(&Default::default(), cx);
 4748    });
 4749
 4750    cx.assert_editor_state(indoc!(
 4751        r#"abc
 4752           defˇghi
 4753
 4754           jk
 4755           nlmˇo
 4756           "#
 4757    ));
 4758
 4759    cx.update_editor(|view, cx| {
 4760        view.add_selection_below(&Default::default(), cx);
 4761    });
 4762
 4763    cx.assert_editor_state(indoc!(
 4764        r#"abc
 4765           defˇghi
 4766
 4767           jk
 4768           nlmˇo
 4769           "#
 4770    ));
 4771
 4772    // change selections
 4773    cx.set_state(indoc!(
 4774        r#"abc
 4775           def«ˇg»hi
 4776
 4777           jk
 4778           nlmo
 4779           "#
 4780    ));
 4781
 4782    cx.update_editor(|view, cx| {
 4783        view.add_selection_below(&Default::default(), cx);
 4784    });
 4785
 4786    cx.assert_editor_state(indoc!(
 4787        r#"abc
 4788           def«ˇg»hi
 4789
 4790           jk
 4791           nlm«ˇo»
 4792           "#
 4793    ));
 4794
 4795    cx.update_editor(|view, cx| {
 4796        view.add_selection_below(&Default::default(), cx);
 4797    });
 4798
 4799    cx.assert_editor_state(indoc!(
 4800        r#"abc
 4801           def«ˇg»hi
 4802
 4803           jk
 4804           nlm«ˇo»
 4805           "#
 4806    ));
 4807
 4808    cx.update_editor(|view, cx| {
 4809        view.add_selection_above(&Default::default(), cx);
 4810    });
 4811
 4812    cx.assert_editor_state(indoc!(
 4813        r#"abc
 4814           def«ˇg»hi
 4815
 4816           jk
 4817           nlmo
 4818           "#
 4819    ));
 4820
 4821    cx.update_editor(|view, cx| {
 4822        view.add_selection_above(&Default::default(), cx);
 4823    });
 4824
 4825    cx.assert_editor_state(indoc!(
 4826        r#"abc
 4827           def«ˇg»hi
 4828
 4829           jk
 4830           nlmo
 4831           "#
 4832    ));
 4833
 4834    // Change selections again
 4835    cx.set_state(indoc!(
 4836        r#"a«bc
 4837           defgˇ»hi
 4838
 4839           jk
 4840           nlmo
 4841           "#
 4842    ));
 4843
 4844    cx.update_editor(|view, cx| {
 4845        view.add_selection_below(&Default::default(), cx);
 4846    });
 4847
 4848    cx.assert_editor_state(indoc!(
 4849        r#"a«bcˇ»
 4850           d«efgˇ»hi
 4851
 4852           j«kˇ»
 4853           nlmo
 4854           "#
 4855    ));
 4856
 4857    cx.update_editor(|view, cx| {
 4858        view.add_selection_below(&Default::default(), cx);
 4859    });
 4860    cx.assert_editor_state(indoc!(
 4861        r#"a«bcˇ»
 4862           d«efgˇ»hi
 4863
 4864           j«kˇ»
 4865           n«lmoˇ»
 4866           "#
 4867    ));
 4868    cx.update_editor(|view, cx| {
 4869        view.add_selection_above(&Default::default(), cx);
 4870    });
 4871
 4872    cx.assert_editor_state(indoc!(
 4873        r#"a«bcˇ»
 4874           d«efgˇ»hi
 4875
 4876           j«kˇ»
 4877           nlmo
 4878           "#
 4879    ));
 4880
 4881    // Change selections again
 4882    cx.set_state(indoc!(
 4883        r#"abc
 4884           d«ˇefghi
 4885
 4886           jk
 4887           nlm»o
 4888           "#
 4889    ));
 4890
 4891    cx.update_editor(|view, cx| {
 4892        view.add_selection_above(&Default::default(), cx);
 4893    });
 4894
 4895    cx.assert_editor_state(indoc!(
 4896        r#"a«ˇbc»
 4897           d«ˇef»ghi
 4898
 4899           j«ˇk»
 4900           n«ˇlm»o
 4901           "#
 4902    ));
 4903
 4904    cx.update_editor(|view, cx| {
 4905        view.add_selection_below(&Default::default(), cx);
 4906    });
 4907
 4908    cx.assert_editor_state(indoc!(
 4909        r#"abc
 4910           d«ˇef»ghi
 4911
 4912           j«ˇk»
 4913           n«ˇlm»o
 4914           "#
 4915    ));
 4916}
 4917
 4918#[gpui::test]
 4919async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4920    init_test(cx, |_| {});
 4921
 4922    let mut cx = EditorTestContext::new(cx).await;
 4923    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4924
 4925    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4926        .unwrap();
 4927    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4928
 4929    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4930        .unwrap();
 4931    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4932
 4933    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4934    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4935
 4936    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4937    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4938
 4939    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4940        .unwrap();
 4941    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4942
 4943    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4944        .unwrap();
 4945    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4946}
 4947
 4948#[gpui::test]
 4949async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4950    init_test(cx, |_| {});
 4951
 4952    let mut cx = EditorTestContext::new(cx).await;
 4953    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4954
 4955    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4956        .unwrap();
 4957    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4958}
 4959
 4960#[gpui::test]
 4961async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4962    init_test(cx, |_| {});
 4963
 4964    let mut cx = EditorTestContext::new(cx).await;
 4965    cx.set_state(
 4966        r#"let foo = 2;
 4967lˇet foo = 2;
 4968let fooˇ = 2;
 4969let foo = 2;
 4970let foo = ˇ2;"#,
 4971    );
 4972
 4973    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4974        .unwrap();
 4975    cx.assert_editor_state(
 4976        r#"let foo = 2;
 4977«letˇ» foo = 2;
 4978let «fooˇ» = 2;
 4979let foo = 2;
 4980let foo = «2ˇ»;"#,
 4981    );
 4982
 4983    // noop for multiple selections with different contents
 4984    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4985        .unwrap();
 4986    cx.assert_editor_state(
 4987        r#"let foo = 2;
 4988«letˇ» foo = 2;
 4989let «fooˇ» = 2;
 4990let foo = 2;
 4991let foo = «2ˇ»;"#,
 4992    );
 4993}
 4994
 4995#[gpui::test]
 4996async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4997    init_test(cx, |_| {});
 4998
 4999    let mut cx =
 5000        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5001
 5002    cx.assert_editor_state(indoc! {"
 5003        ˇbbb
 5004        ccc
 5005
 5006        bbb
 5007        ccc
 5008        "});
 5009    cx.dispatch_action(SelectPrevious::default());
 5010    cx.assert_editor_state(indoc! {"
 5011                «bbbˇ»
 5012                ccc
 5013
 5014                bbb
 5015                ccc
 5016                "});
 5017    cx.dispatch_action(SelectPrevious::default());
 5018    cx.assert_editor_state(indoc! {"
 5019                «bbbˇ»
 5020                ccc
 5021
 5022                «bbbˇ»
 5023                ccc
 5024                "});
 5025}
 5026
 5027#[gpui::test]
 5028async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5029    init_test(cx, |_| {});
 5030
 5031    let mut cx = EditorTestContext::new(cx).await;
 5032    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5033
 5034    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5035        .unwrap();
 5036    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5037
 5038    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5039        .unwrap();
 5040    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5041
 5042    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5043    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5044
 5045    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5046    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5047
 5048    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5049        .unwrap();
 5050    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5051
 5052    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5053        .unwrap();
 5054    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5055
 5056    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5057        .unwrap();
 5058    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5059}
 5060
 5061#[gpui::test]
 5062async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5063    init_test(cx, |_| {});
 5064
 5065    let mut cx = EditorTestContext::new(cx).await;
 5066    cx.set_state(
 5067        r#"let foo = 2;
 5068lˇet foo = 2;
 5069let fooˇ = 2;
 5070let foo = 2;
 5071let foo = ˇ2;"#,
 5072    );
 5073
 5074    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5075        .unwrap();
 5076    cx.assert_editor_state(
 5077        r#"let foo = 2;
 5078«letˇ» foo = 2;
 5079let «fooˇ» = 2;
 5080let foo = 2;
 5081let foo = «2ˇ»;"#,
 5082    );
 5083
 5084    // noop for multiple selections with different contents
 5085    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5086        .unwrap();
 5087    cx.assert_editor_state(
 5088        r#"let foo = 2;
 5089«letˇ» foo = 2;
 5090let «fooˇ» = 2;
 5091let foo = 2;
 5092let foo = «2ˇ»;"#,
 5093    );
 5094}
 5095
 5096#[gpui::test]
 5097async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5098    init_test(cx, |_| {});
 5099
 5100    let mut cx = EditorTestContext::new(cx).await;
 5101    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5102
 5103    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5104        .unwrap();
 5105    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5106
 5107    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5108        .unwrap();
 5109    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5110
 5111    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5112    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5113
 5114    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5115    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5116
 5117    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5118        .unwrap();
 5119    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5120
 5121    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5122        .unwrap();
 5123    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5124}
 5125
 5126#[gpui::test]
 5127async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5128    init_test(cx, |_| {});
 5129
 5130    let language = Arc::new(Language::new(
 5131        LanguageConfig::default(),
 5132        Some(tree_sitter_rust::LANGUAGE.into()),
 5133    ));
 5134
 5135    let text = r#"
 5136        use mod1::mod2::{mod3, mod4};
 5137
 5138        fn fn_1(param1: bool, param2: &str) {
 5139            let var1 = "text";
 5140        }
 5141    "#
 5142    .unindent();
 5143
 5144    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5145    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5146    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5147
 5148    editor
 5149        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5150        .await;
 5151
 5152    editor.update(cx, |view, cx| {
 5153        view.change_selections(None, cx, |s| {
 5154            s.select_display_ranges([
 5155                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5156                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5157                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5158            ]);
 5159        });
 5160        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5161    });
 5162    editor.update(cx, |editor, cx| {
 5163        assert_text_with_selections(
 5164            editor,
 5165            indoc! {r#"
 5166                use mod1::mod2::{mod3, «mod4ˇ»};
 5167
 5168                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5169                    let var1 = "«textˇ»";
 5170                }
 5171            "#},
 5172            cx,
 5173        );
 5174    });
 5175
 5176    editor.update(cx, |view, cx| {
 5177        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5178    });
 5179    editor.update(cx, |editor, cx| {
 5180        assert_text_with_selections(
 5181            editor,
 5182            indoc! {r#"
 5183                use mod1::mod2::«{mod3, mod4}ˇ»;
 5184
 5185                «ˇfn fn_1(param1: bool, param2: &str) {
 5186                    let var1 = "text";
 5187 5188            "#},
 5189            cx,
 5190        );
 5191    });
 5192
 5193    editor.update(cx, |view, cx| {
 5194        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5195    });
 5196    assert_eq!(
 5197        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5198        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5199    );
 5200
 5201    // Trying to expand the selected syntax node one more time has no effect.
 5202    editor.update(cx, |view, cx| {
 5203        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5204    });
 5205    assert_eq!(
 5206        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5207        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5208    );
 5209
 5210    editor.update(cx, |view, cx| {
 5211        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5212    });
 5213    editor.update(cx, |editor, cx| {
 5214        assert_text_with_selections(
 5215            editor,
 5216            indoc! {r#"
 5217                use mod1::mod2::«{mod3, mod4}ˇ»;
 5218
 5219                «ˇfn fn_1(param1: bool, param2: &str) {
 5220                    let var1 = "text";
 5221 5222            "#},
 5223            cx,
 5224        );
 5225    });
 5226
 5227    editor.update(cx, |view, cx| {
 5228        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5229    });
 5230    editor.update(cx, |editor, cx| {
 5231        assert_text_with_selections(
 5232            editor,
 5233            indoc! {r#"
 5234                use mod1::mod2::{mod3, «mod4ˇ»};
 5235
 5236                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5237                    let var1 = "«textˇ»";
 5238                }
 5239            "#},
 5240            cx,
 5241        );
 5242    });
 5243
 5244    editor.update(cx, |view, cx| {
 5245        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5246    });
 5247    editor.update(cx, |editor, cx| {
 5248        assert_text_with_selections(
 5249            editor,
 5250            indoc! {r#"
 5251                use mod1::mod2::{mod3, mo«ˇ»d4};
 5252
 5253                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5254                    let var1 = "te«ˇ»xt";
 5255                }
 5256            "#},
 5257            cx,
 5258        );
 5259    });
 5260
 5261    // Trying to shrink the selected syntax node one more time has no effect.
 5262    editor.update(cx, |view, cx| {
 5263        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5264    });
 5265    editor.update(cx, |editor, cx| {
 5266        assert_text_with_selections(
 5267            editor,
 5268            indoc! {r#"
 5269                use mod1::mod2::{mod3, mo«ˇ»d4};
 5270
 5271                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5272                    let var1 = "te«ˇ»xt";
 5273                }
 5274            "#},
 5275            cx,
 5276        );
 5277    });
 5278
 5279    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5280    // a fold.
 5281    editor.update(cx, |view, cx| {
 5282        view.fold_ranges(
 5283            vec![
 5284                (
 5285                    Point::new(0, 21)..Point::new(0, 24),
 5286                    FoldPlaceholder::test(),
 5287                ),
 5288                (
 5289                    Point::new(3, 20)..Point::new(3, 22),
 5290                    FoldPlaceholder::test(),
 5291                ),
 5292            ],
 5293            true,
 5294            cx,
 5295        );
 5296        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5297    });
 5298    editor.update(cx, |editor, cx| {
 5299        assert_text_with_selections(
 5300            editor,
 5301            indoc! {r#"
 5302                use mod1::mod2::«{mod3, mod4}ˇ»;
 5303
 5304                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5305                    «let var1 = "text";ˇ»
 5306                }
 5307            "#},
 5308            cx,
 5309        );
 5310    });
 5311}
 5312
 5313#[gpui::test]
 5314async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5315    init_test(cx, |_| {});
 5316
 5317    let language = Arc::new(
 5318        Language::new(
 5319            LanguageConfig {
 5320                brackets: BracketPairConfig {
 5321                    pairs: vec![
 5322                        BracketPair {
 5323                            start: "{".to_string(),
 5324                            end: "}".to_string(),
 5325                            close: false,
 5326                            surround: false,
 5327                            newline: true,
 5328                        },
 5329                        BracketPair {
 5330                            start: "(".to_string(),
 5331                            end: ")".to_string(),
 5332                            close: false,
 5333                            surround: false,
 5334                            newline: true,
 5335                        },
 5336                    ],
 5337                    ..Default::default()
 5338                },
 5339                ..Default::default()
 5340            },
 5341            Some(tree_sitter_rust::LANGUAGE.into()),
 5342        )
 5343        .with_indents_query(
 5344            r#"
 5345                (_ "(" ")" @end) @indent
 5346                (_ "{" "}" @end) @indent
 5347            "#,
 5348        )
 5349        .unwrap(),
 5350    );
 5351
 5352    let text = "fn a() {}";
 5353
 5354    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5355    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5356    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5357    editor
 5358        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5359        .await;
 5360
 5361    editor.update(cx, |editor, cx| {
 5362        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5363        editor.newline(&Newline, cx);
 5364        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5365        assert_eq!(
 5366            editor.selections.ranges(cx),
 5367            &[
 5368                Point::new(1, 4)..Point::new(1, 4),
 5369                Point::new(3, 4)..Point::new(3, 4),
 5370                Point::new(5, 0)..Point::new(5, 0)
 5371            ]
 5372        );
 5373    });
 5374}
 5375
 5376#[gpui::test]
 5377async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5378    init_test(cx, |_| {});
 5379
 5380    let mut cx = EditorTestContext::new(cx).await;
 5381
 5382    let language = Arc::new(Language::new(
 5383        LanguageConfig {
 5384            brackets: BracketPairConfig {
 5385                pairs: vec![
 5386                    BracketPair {
 5387                        start: "{".to_string(),
 5388                        end: "}".to_string(),
 5389                        close: true,
 5390                        surround: true,
 5391                        newline: true,
 5392                    },
 5393                    BracketPair {
 5394                        start: "(".to_string(),
 5395                        end: ")".to_string(),
 5396                        close: true,
 5397                        surround: true,
 5398                        newline: true,
 5399                    },
 5400                    BracketPair {
 5401                        start: "/*".to_string(),
 5402                        end: " */".to_string(),
 5403                        close: true,
 5404                        surround: true,
 5405                        newline: true,
 5406                    },
 5407                    BracketPair {
 5408                        start: "[".to_string(),
 5409                        end: "]".to_string(),
 5410                        close: false,
 5411                        surround: false,
 5412                        newline: true,
 5413                    },
 5414                    BracketPair {
 5415                        start: "\"".to_string(),
 5416                        end: "\"".to_string(),
 5417                        close: true,
 5418                        surround: true,
 5419                        newline: false,
 5420                    },
 5421                    BracketPair {
 5422                        start: "<".to_string(),
 5423                        end: ">".to_string(),
 5424                        close: false,
 5425                        surround: true,
 5426                        newline: true,
 5427                    },
 5428                ],
 5429                ..Default::default()
 5430            },
 5431            autoclose_before: "})]".to_string(),
 5432            ..Default::default()
 5433        },
 5434        Some(tree_sitter_rust::LANGUAGE.into()),
 5435    ));
 5436
 5437    cx.language_registry().add(language.clone());
 5438    cx.update_buffer(|buffer, cx| {
 5439        buffer.set_language(Some(language), cx);
 5440    });
 5441
 5442    cx.set_state(
 5443        &r#"
 5444            🏀ˇ
 5445            εˇ
 5446            ❤️ˇ
 5447        "#
 5448        .unindent(),
 5449    );
 5450
 5451    // autoclose multiple nested brackets at multiple cursors
 5452    cx.update_editor(|view, cx| {
 5453        view.handle_input("{", cx);
 5454        view.handle_input("{", cx);
 5455        view.handle_input("{", cx);
 5456    });
 5457    cx.assert_editor_state(
 5458        &"
 5459            🏀{{{ˇ}}}
 5460            ε{{{ˇ}}}
 5461            ❤️{{{ˇ}}}
 5462        "
 5463        .unindent(),
 5464    );
 5465
 5466    // insert a different closing bracket
 5467    cx.update_editor(|view, cx| {
 5468        view.handle_input(")", cx);
 5469    });
 5470    cx.assert_editor_state(
 5471        &"
 5472            🏀{{{)ˇ}}}
 5473            ε{{{)ˇ}}}
 5474            ❤️{{{)ˇ}}}
 5475        "
 5476        .unindent(),
 5477    );
 5478
 5479    // skip over the auto-closed brackets when typing a closing bracket
 5480    cx.update_editor(|view, cx| {
 5481        view.move_right(&MoveRight, cx);
 5482        view.handle_input("}", cx);
 5483        view.handle_input("}", cx);
 5484        view.handle_input("}", cx);
 5485    });
 5486    cx.assert_editor_state(
 5487        &"
 5488            🏀{{{)}}}}ˇ
 5489            ε{{{)}}}}ˇ
 5490            ❤️{{{)}}}}ˇ
 5491        "
 5492        .unindent(),
 5493    );
 5494
 5495    // autoclose multi-character pairs
 5496    cx.set_state(
 5497        &"
 5498            ˇ
 5499            ˇ
 5500        "
 5501        .unindent(),
 5502    );
 5503    cx.update_editor(|view, cx| {
 5504        view.handle_input("/", cx);
 5505        view.handle_input("*", cx);
 5506    });
 5507    cx.assert_editor_state(
 5508        &"
 5509            /*ˇ */
 5510            /*ˇ */
 5511        "
 5512        .unindent(),
 5513    );
 5514
 5515    // one cursor autocloses a multi-character pair, one cursor
 5516    // does not autoclose.
 5517    cx.set_state(
 5518        &"
 5519 5520            ˇ
 5521        "
 5522        .unindent(),
 5523    );
 5524    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5525    cx.assert_editor_state(
 5526        &"
 5527            /*ˇ */
 5528 5529        "
 5530        .unindent(),
 5531    );
 5532
 5533    // Don't autoclose if the next character isn't whitespace and isn't
 5534    // listed in the language's "autoclose_before" section.
 5535    cx.set_state("ˇa b");
 5536    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5537    cx.assert_editor_state("{ˇa b");
 5538
 5539    // Don't autoclose if `close` is false for the bracket pair
 5540    cx.set_state("ˇ");
 5541    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5542    cx.assert_editor_state("");
 5543
 5544    // Surround with brackets if text is selected
 5545    cx.set_state("«aˇ» b");
 5546    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5547    cx.assert_editor_state("{«aˇ»} b");
 5548
 5549    // Autclose pair where the start and end characters are the same
 5550    cx.set_state("");
 5551    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5552    cx.assert_editor_state("a\"ˇ\"");
 5553    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5554    cx.assert_editor_state("a\"\"ˇ");
 5555
 5556    // Don't autoclose pair if autoclose is disabled
 5557    cx.set_state("ˇ");
 5558    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5559    cx.assert_editor_state("");
 5560
 5561    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5562    cx.set_state("«aˇ» b");
 5563    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5564    cx.assert_editor_state("<«aˇ»> b");
 5565}
 5566
 5567#[gpui::test]
 5568async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5569    init_test(cx, |settings| {
 5570        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5571    });
 5572
 5573    let mut cx = EditorTestContext::new(cx).await;
 5574
 5575    let language = Arc::new(Language::new(
 5576        LanguageConfig {
 5577            brackets: BracketPairConfig {
 5578                pairs: vec![
 5579                    BracketPair {
 5580                        start: "{".to_string(),
 5581                        end: "}".to_string(),
 5582                        close: true,
 5583                        surround: true,
 5584                        newline: true,
 5585                    },
 5586                    BracketPair {
 5587                        start: "(".to_string(),
 5588                        end: ")".to_string(),
 5589                        close: true,
 5590                        surround: true,
 5591                        newline: true,
 5592                    },
 5593                    BracketPair {
 5594                        start: "[".to_string(),
 5595                        end: "]".to_string(),
 5596                        close: false,
 5597                        surround: false,
 5598                        newline: true,
 5599                    },
 5600                ],
 5601                ..Default::default()
 5602            },
 5603            autoclose_before: "})]".to_string(),
 5604            ..Default::default()
 5605        },
 5606        Some(tree_sitter_rust::LANGUAGE.into()),
 5607    ));
 5608
 5609    cx.language_registry().add(language.clone());
 5610    cx.update_buffer(|buffer, cx| {
 5611        buffer.set_language(Some(language), cx);
 5612    });
 5613
 5614    cx.set_state(
 5615        &"
 5616            ˇ
 5617            ˇ
 5618            ˇ
 5619        "
 5620        .unindent(),
 5621    );
 5622
 5623    // ensure only matching closing brackets are skipped over
 5624    cx.update_editor(|view, cx| {
 5625        view.handle_input("}", cx);
 5626        view.move_left(&MoveLeft, cx);
 5627        view.handle_input(")", cx);
 5628        view.move_left(&MoveLeft, cx);
 5629    });
 5630    cx.assert_editor_state(
 5631        &"
 5632            ˇ)}
 5633            ˇ)}
 5634            ˇ)}
 5635        "
 5636        .unindent(),
 5637    );
 5638
 5639    // skip-over closing brackets at multiple cursors
 5640    cx.update_editor(|view, cx| {
 5641        view.handle_input(")", cx);
 5642        view.handle_input("}", cx);
 5643    });
 5644    cx.assert_editor_state(
 5645        &"
 5646            )}ˇ
 5647            )}ˇ
 5648            )}ˇ
 5649        "
 5650        .unindent(),
 5651    );
 5652
 5653    // ignore non-close brackets
 5654    cx.update_editor(|view, cx| {
 5655        view.handle_input("]", cx);
 5656        view.move_left(&MoveLeft, cx);
 5657        view.handle_input("]", cx);
 5658    });
 5659    cx.assert_editor_state(
 5660        &"
 5661            )}]ˇ]
 5662            )}]ˇ]
 5663            )}]ˇ]
 5664        "
 5665        .unindent(),
 5666    );
 5667}
 5668
 5669#[gpui::test]
 5670async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5671    init_test(cx, |_| {});
 5672
 5673    let mut cx = EditorTestContext::new(cx).await;
 5674
 5675    let html_language = Arc::new(
 5676        Language::new(
 5677            LanguageConfig {
 5678                name: "HTML".into(),
 5679                brackets: BracketPairConfig {
 5680                    pairs: vec![
 5681                        BracketPair {
 5682                            start: "<".into(),
 5683                            end: ">".into(),
 5684                            close: true,
 5685                            ..Default::default()
 5686                        },
 5687                        BracketPair {
 5688                            start: "{".into(),
 5689                            end: "}".into(),
 5690                            close: true,
 5691                            ..Default::default()
 5692                        },
 5693                        BracketPair {
 5694                            start: "(".into(),
 5695                            end: ")".into(),
 5696                            close: true,
 5697                            ..Default::default()
 5698                        },
 5699                    ],
 5700                    ..Default::default()
 5701                },
 5702                autoclose_before: "})]>".into(),
 5703                ..Default::default()
 5704            },
 5705            Some(tree_sitter_html::language()),
 5706        )
 5707        .with_injection_query(
 5708            r#"
 5709            (script_element
 5710                (raw_text) @content
 5711                (#set! "language" "javascript"))
 5712            "#,
 5713        )
 5714        .unwrap(),
 5715    );
 5716
 5717    let javascript_language = Arc::new(Language::new(
 5718        LanguageConfig {
 5719            name: "JavaScript".into(),
 5720            brackets: BracketPairConfig {
 5721                pairs: vec![
 5722                    BracketPair {
 5723                        start: "/*".into(),
 5724                        end: " */".into(),
 5725                        close: true,
 5726                        ..Default::default()
 5727                    },
 5728                    BracketPair {
 5729                        start: "{".into(),
 5730                        end: "}".into(),
 5731                        close: true,
 5732                        ..Default::default()
 5733                    },
 5734                    BracketPair {
 5735                        start: "(".into(),
 5736                        end: ")".into(),
 5737                        close: true,
 5738                        ..Default::default()
 5739                    },
 5740                ],
 5741                ..Default::default()
 5742            },
 5743            autoclose_before: "})]>".into(),
 5744            ..Default::default()
 5745        },
 5746        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5747    ));
 5748
 5749    cx.language_registry().add(html_language.clone());
 5750    cx.language_registry().add(javascript_language.clone());
 5751
 5752    cx.update_buffer(|buffer, cx| {
 5753        buffer.set_language(Some(html_language), cx);
 5754    });
 5755
 5756    cx.set_state(
 5757        &r#"
 5758            <body>ˇ
 5759                <script>
 5760                    var x = 1;ˇ
 5761                </script>
 5762            </body>ˇ
 5763        "#
 5764        .unindent(),
 5765    );
 5766
 5767    // Precondition: different languages are active at different locations.
 5768    cx.update_editor(|editor, cx| {
 5769        let snapshot = editor.snapshot(cx);
 5770        let cursors = editor.selections.ranges::<usize>(cx);
 5771        let languages = cursors
 5772            .iter()
 5773            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5774            .collect::<Vec<_>>();
 5775        assert_eq!(
 5776            languages,
 5777            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5778        );
 5779    });
 5780
 5781    // Angle brackets autoclose in HTML, but not JavaScript.
 5782    cx.update_editor(|editor, cx| {
 5783        editor.handle_input("<", cx);
 5784        editor.handle_input("a", cx);
 5785    });
 5786    cx.assert_editor_state(
 5787        &r#"
 5788            <body><aˇ>
 5789                <script>
 5790                    var x = 1;<aˇ
 5791                </script>
 5792            </body><aˇ>
 5793        "#
 5794        .unindent(),
 5795    );
 5796
 5797    // Curly braces and parens autoclose in both HTML and JavaScript.
 5798    cx.update_editor(|editor, cx| {
 5799        editor.handle_input(" b=", cx);
 5800        editor.handle_input("{", cx);
 5801        editor.handle_input("c", cx);
 5802        editor.handle_input("(", cx);
 5803    });
 5804    cx.assert_editor_state(
 5805        &r#"
 5806            <body><a b={c(ˇ)}>
 5807                <script>
 5808                    var x = 1;<a b={c(ˇ)}
 5809                </script>
 5810            </body><a b={c(ˇ)}>
 5811        "#
 5812        .unindent(),
 5813    );
 5814
 5815    // Brackets that were already autoclosed are skipped.
 5816    cx.update_editor(|editor, cx| {
 5817        editor.handle_input(")", cx);
 5818        editor.handle_input("d", cx);
 5819        editor.handle_input("}", cx);
 5820    });
 5821    cx.assert_editor_state(
 5822        &r#"
 5823            <body><a b={c()d}ˇ>
 5824                <script>
 5825                    var x = 1;<a b={c()d}ˇ
 5826                </script>
 5827            </body><a b={c()d}ˇ>
 5828        "#
 5829        .unindent(),
 5830    );
 5831    cx.update_editor(|editor, cx| {
 5832        editor.handle_input(">", cx);
 5833    });
 5834    cx.assert_editor_state(
 5835        &r#"
 5836            <body><a b={c()d}>ˇ
 5837                <script>
 5838                    var x = 1;<a b={c()d}>ˇ
 5839                </script>
 5840            </body><a b={c()d}>ˇ
 5841        "#
 5842        .unindent(),
 5843    );
 5844
 5845    // Reset
 5846    cx.set_state(
 5847        &r#"
 5848            <body>ˇ
 5849                <script>
 5850                    var x = 1;ˇ
 5851                </script>
 5852            </body>ˇ
 5853        "#
 5854        .unindent(),
 5855    );
 5856
 5857    cx.update_editor(|editor, cx| {
 5858        editor.handle_input("<", cx);
 5859    });
 5860    cx.assert_editor_state(
 5861        &r#"
 5862            <body><ˇ>
 5863                <script>
 5864                    var x = 1;<ˇ
 5865                </script>
 5866            </body><ˇ>
 5867        "#
 5868        .unindent(),
 5869    );
 5870
 5871    // When backspacing, the closing angle brackets are removed.
 5872    cx.update_editor(|editor, cx| {
 5873        editor.backspace(&Backspace, cx);
 5874    });
 5875    cx.assert_editor_state(
 5876        &r#"
 5877            <body>ˇ
 5878                <script>
 5879                    var x = 1;ˇ
 5880                </script>
 5881            </body>ˇ
 5882        "#
 5883        .unindent(),
 5884    );
 5885
 5886    // Block comments autoclose in JavaScript, but not HTML.
 5887    cx.update_editor(|editor, cx| {
 5888        editor.handle_input("/", cx);
 5889        editor.handle_input("*", cx);
 5890    });
 5891    cx.assert_editor_state(
 5892        &r#"
 5893            <body>/*ˇ
 5894                <script>
 5895                    var x = 1;/*ˇ */
 5896                </script>
 5897            </body>/*ˇ
 5898        "#
 5899        .unindent(),
 5900    );
 5901}
 5902
 5903#[gpui::test]
 5904async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5905    init_test(cx, |_| {});
 5906
 5907    let mut cx = EditorTestContext::new(cx).await;
 5908
 5909    let rust_language = Arc::new(
 5910        Language::new(
 5911            LanguageConfig {
 5912                name: "Rust".into(),
 5913                brackets: serde_json::from_value(json!([
 5914                    { "start": "{", "end": "}", "close": true, "newline": true },
 5915                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5916                ]))
 5917                .unwrap(),
 5918                autoclose_before: "})]>".into(),
 5919                ..Default::default()
 5920            },
 5921            Some(tree_sitter_rust::LANGUAGE.into()),
 5922        )
 5923        .with_override_query("(string_literal) @string")
 5924        .unwrap(),
 5925    );
 5926
 5927    cx.language_registry().add(rust_language.clone());
 5928    cx.update_buffer(|buffer, cx| {
 5929        buffer.set_language(Some(rust_language), cx);
 5930    });
 5931
 5932    cx.set_state(
 5933        &r#"
 5934            let x = ˇ
 5935        "#
 5936        .unindent(),
 5937    );
 5938
 5939    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5940    cx.update_editor(|editor, cx| {
 5941        editor.handle_input("\"", cx);
 5942    });
 5943    cx.assert_editor_state(
 5944        &r#"
 5945            let x = "ˇ"
 5946        "#
 5947        .unindent(),
 5948    );
 5949
 5950    // Inserting another quotation mark. The cursor moves across the existing
 5951    // automatically-inserted quotation mark.
 5952    cx.update_editor(|editor, cx| {
 5953        editor.handle_input("\"", cx);
 5954    });
 5955    cx.assert_editor_state(
 5956        &r#"
 5957            let x = ""ˇ
 5958        "#
 5959        .unindent(),
 5960    );
 5961
 5962    // Reset
 5963    cx.set_state(
 5964        &r#"
 5965            let x = ˇ
 5966        "#
 5967        .unindent(),
 5968    );
 5969
 5970    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5971    cx.update_editor(|editor, cx| {
 5972        editor.handle_input("\"", cx);
 5973        editor.handle_input(" ", cx);
 5974        editor.move_left(&Default::default(), cx);
 5975        editor.handle_input("\\", cx);
 5976        editor.handle_input("\"", cx);
 5977    });
 5978    cx.assert_editor_state(
 5979        &r#"
 5980            let x = "\"ˇ "
 5981        "#
 5982        .unindent(),
 5983    );
 5984
 5985    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5986    // mark. Nothing is inserted.
 5987    cx.update_editor(|editor, cx| {
 5988        editor.move_right(&Default::default(), cx);
 5989        editor.handle_input("\"", cx);
 5990    });
 5991    cx.assert_editor_state(
 5992        &r#"
 5993            let x = "\" "ˇ
 5994        "#
 5995        .unindent(),
 5996    );
 5997}
 5998
 5999#[gpui::test]
 6000async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6001    init_test(cx, |_| {});
 6002
 6003    let language = Arc::new(Language::new(
 6004        LanguageConfig {
 6005            brackets: BracketPairConfig {
 6006                pairs: vec![
 6007                    BracketPair {
 6008                        start: "{".to_string(),
 6009                        end: "}".to_string(),
 6010                        close: true,
 6011                        surround: true,
 6012                        newline: true,
 6013                    },
 6014                    BracketPair {
 6015                        start: "/* ".to_string(),
 6016                        end: "*/".to_string(),
 6017                        close: true,
 6018                        surround: true,
 6019                        ..Default::default()
 6020                    },
 6021                ],
 6022                ..Default::default()
 6023            },
 6024            ..Default::default()
 6025        },
 6026        Some(tree_sitter_rust::LANGUAGE.into()),
 6027    ));
 6028
 6029    let text = r#"
 6030        a
 6031        b
 6032        c
 6033    "#
 6034    .unindent();
 6035
 6036    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6037    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6038    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6039    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6040        .await;
 6041
 6042    view.update(cx, |view, cx| {
 6043        view.change_selections(None, cx, |s| {
 6044            s.select_display_ranges([
 6045                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6046                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6047                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6048            ])
 6049        });
 6050
 6051        view.handle_input("{", cx);
 6052        view.handle_input("{", cx);
 6053        view.handle_input("{", cx);
 6054        assert_eq!(
 6055            view.text(cx),
 6056            "
 6057                {{{a}}}
 6058                {{{b}}}
 6059                {{{c}}}
 6060            "
 6061            .unindent()
 6062        );
 6063        assert_eq!(
 6064            view.selections.display_ranges(cx),
 6065            [
 6066                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6067                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6068                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6069            ]
 6070        );
 6071
 6072        view.undo(&Undo, cx);
 6073        view.undo(&Undo, cx);
 6074        view.undo(&Undo, cx);
 6075        assert_eq!(
 6076            view.text(cx),
 6077            "
 6078                a
 6079                b
 6080                c
 6081            "
 6082            .unindent()
 6083        );
 6084        assert_eq!(
 6085            view.selections.display_ranges(cx),
 6086            [
 6087                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6088                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6089                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6090            ]
 6091        );
 6092
 6093        // Ensure inserting the first character of a multi-byte bracket pair
 6094        // doesn't surround the selections with the bracket.
 6095        view.handle_input("/", cx);
 6096        assert_eq!(
 6097            view.text(cx),
 6098            "
 6099                /
 6100                /
 6101                /
 6102            "
 6103            .unindent()
 6104        );
 6105        assert_eq!(
 6106            view.selections.display_ranges(cx),
 6107            [
 6108                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6109                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6110                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6111            ]
 6112        );
 6113
 6114        view.undo(&Undo, cx);
 6115        assert_eq!(
 6116            view.text(cx),
 6117            "
 6118                a
 6119                b
 6120                c
 6121            "
 6122            .unindent()
 6123        );
 6124        assert_eq!(
 6125            view.selections.display_ranges(cx),
 6126            [
 6127                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6128                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6129                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6130            ]
 6131        );
 6132
 6133        // Ensure inserting the last character of a multi-byte bracket pair
 6134        // doesn't surround the selections with the bracket.
 6135        view.handle_input("*", cx);
 6136        assert_eq!(
 6137            view.text(cx),
 6138            "
 6139                *
 6140                *
 6141                *
 6142            "
 6143            .unindent()
 6144        );
 6145        assert_eq!(
 6146            view.selections.display_ranges(cx),
 6147            [
 6148                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6149                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6150                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6151            ]
 6152        );
 6153    });
 6154}
 6155
 6156#[gpui::test]
 6157async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6158    init_test(cx, |_| {});
 6159
 6160    let language = Arc::new(Language::new(
 6161        LanguageConfig {
 6162            brackets: BracketPairConfig {
 6163                pairs: vec![BracketPair {
 6164                    start: "{".to_string(),
 6165                    end: "}".to_string(),
 6166                    close: true,
 6167                    surround: true,
 6168                    newline: true,
 6169                }],
 6170                ..Default::default()
 6171            },
 6172            autoclose_before: "}".to_string(),
 6173            ..Default::default()
 6174        },
 6175        Some(tree_sitter_rust::LANGUAGE.into()),
 6176    ));
 6177
 6178    let text = r#"
 6179        a
 6180        b
 6181        c
 6182    "#
 6183    .unindent();
 6184
 6185    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6186    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6187    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6188    editor
 6189        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6190        .await;
 6191
 6192    editor.update(cx, |editor, cx| {
 6193        editor.change_selections(None, cx, |s| {
 6194            s.select_ranges([
 6195                Point::new(0, 1)..Point::new(0, 1),
 6196                Point::new(1, 1)..Point::new(1, 1),
 6197                Point::new(2, 1)..Point::new(2, 1),
 6198            ])
 6199        });
 6200
 6201        editor.handle_input("{", cx);
 6202        editor.handle_input("{", cx);
 6203        editor.handle_input("_", cx);
 6204        assert_eq!(
 6205            editor.text(cx),
 6206            "
 6207                a{{_}}
 6208                b{{_}}
 6209                c{{_}}
 6210            "
 6211            .unindent()
 6212        );
 6213        assert_eq!(
 6214            editor.selections.ranges::<Point>(cx),
 6215            [
 6216                Point::new(0, 4)..Point::new(0, 4),
 6217                Point::new(1, 4)..Point::new(1, 4),
 6218                Point::new(2, 4)..Point::new(2, 4)
 6219            ]
 6220        );
 6221
 6222        editor.backspace(&Default::default(), cx);
 6223        editor.backspace(&Default::default(), cx);
 6224        assert_eq!(
 6225            editor.text(cx),
 6226            "
 6227                a{}
 6228                b{}
 6229                c{}
 6230            "
 6231            .unindent()
 6232        );
 6233        assert_eq!(
 6234            editor.selections.ranges::<Point>(cx),
 6235            [
 6236                Point::new(0, 2)..Point::new(0, 2),
 6237                Point::new(1, 2)..Point::new(1, 2),
 6238                Point::new(2, 2)..Point::new(2, 2)
 6239            ]
 6240        );
 6241
 6242        editor.delete_to_previous_word_start(&Default::default(), cx);
 6243        assert_eq!(
 6244            editor.text(cx),
 6245            "
 6246                a
 6247                b
 6248                c
 6249            "
 6250            .unindent()
 6251        );
 6252        assert_eq!(
 6253            editor.selections.ranges::<Point>(cx),
 6254            [
 6255                Point::new(0, 1)..Point::new(0, 1),
 6256                Point::new(1, 1)..Point::new(1, 1),
 6257                Point::new(2, 1)..Point::new(2, 1)
 6258            ]
 6259        );
 6260    });
 6261}
 6262
 6263#[gpui::test]
 6264async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6265    init_test(cx, |settings| {
 6266        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6267    });
 6268
 6269    let mut cx = EditorTestContext::new(cx).await;
 6270
 6271    let language = Arc::new(Language::new(
 6272        LanguageConfig {
 6273            brackets: BracketPairConfig {
 6274                pairs: vec![
 6275                    BracketPair {
 6276                        start: "{".to_string(),
 6277                        end: "}".to_string(),
 6278                        close: true,
 6279                        surround: true,
 6280                        newline: true,
 6281                    },
 6282                    BracketPair {
 6283                        start: "(".to_string(),
 6284                        end: ")".to_string(),
 6285                        close: true,
 6286                        surround: true,
 6287                        newline: true,
 6288                    },
 6289                    BracketPair {
 6290                        start: "[".to_string(),
 6291                        end: "]".to_string(),
 6292                        close: false,
 6293                        surround: true,
 6294                        newline: true,
 6295                    },
 6296                ],
 6297                ..Default::default()
 6298            },
 6299            autoclose_before: "})]".to_string(),
 6300            ..Default::default()
 6301        },
 6302        Some(tree_sitter_rust::LANGUAGE.into()),
 6303    ));
 6304
 6305    cx.language_registry().add(language.clone());
 6306    cx.update_buffer(|buffer, cx| {
 6307        buffer.set_language(Some(language), cx);
 6308    });
 6309
 6310    cx.set_state(
 6311        &"
 6312            {(ˇ)}
 6313            [[ˇ]]
 6314            {(ˇ)}
 6315        "
 6316        .unindent(),
 6317    );
 6318
 6319    cx.update_editor(|view, cx| {
 6320        view.backspace(&Default::default(), cx);
 6321        view.backspace(&Default::default(), cx);
 6322    });
 6323
 6324    cx.assert_editor_state(
 6325        &"
 6326            ˇ
 6327            ˇ]]
 6328            ˇ
 6329        "
 6330        .unindent(),
 6331    );
 6332
 6333    cx.update_editor(|view, cx| {
 6334        view.handle_input("{", cx);
 6335        view.handle_input("{", cx);
 6336        view.move_right(&MoveRight, cx);
 6337        view.move_right(&MoveRight, cx);
 6338        view.move_left(&MoveLeft, cx);
 6339        view.move_left(&MoveLeft, cx);
 6340        view.backspace(&Default::default(), cx);
 6341    });
 6342
 6343    cx.assert_editor_state(
 6344        &"
 6345            {ˇ}
 6346            {ˇ}]]
 6347            {ˇ}
 6348        "
 6349        .unindent(),
 6350    );
 6351
 6352    cx.update_editor(|view, cx| {
 6353        view.backspace(&Default::default(), cx);
 6354    });
 6355
 6356    cx.assert_editor_state(
 6357        &"
 6358            ˇ
 6359            ˇ]]
 6360            ˇ
 6361        "
 6362        .unindent(),
 6363    );
 6364}
 6365
 6366#[gpui::test]
 6367async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6368    init_test(cx, |_| {});
 6369
 6370    let language = Arc::new(Language::new(
 6371        LanguageConfig::default(),
 6372        Some(tree_sitter_rust::LANGUAGE.into()),
 6373    ));
 6374
 6375    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6376    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6377    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6378    editor
 6379        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6380        .await;
 6381
 6382    editor.update(cx, |editor, cx| {
 6383        editor.set_auto_replace_emoji_shortcode(true);
 6384
 6385        editor.handle_input("Hello ", cx);
 6386        editor.handle_input(":wave", cx);
 6387        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6388
 6389        editor.handle_input(":", cx);
 6390        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6391
 6392        editor.handle_input(" :smile", cx);
 6393        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6394
 6395        editor.handle_input(":", cx);
 6396        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6397
 6398        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6399        editor.handle_input(":wave", cx);
 6400        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6401
 6402        editor.handle_input(":", cx);
 6403        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6404
 6405        editor.handle_input(":1", cx);
 6406        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6407
 6408        editor.handle_input(":", cx);
 6409        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6410
 6411        // Ensure shortcode does not get replaced when it is part of a word
 6412        editor.handle_input(" Test:wave", cx);
 6413        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6414
 6415        editor.handle_input(":", cx);
 6416        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6417
 6418        editor.set_auto_replace_emoji_shortcode(false);
 6419
 6420        // Ensure shortcode does not get replaced when auto replace is off
 6421        editor.handle_input(" :wave", cx);
 6422        assert_eq!(
 6423            editor.text(cx),
 6424            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6425        );
 6426
 6427        editor.handle_input(":", cx);
 6428        assert_eq!(
 6429            editor.text(cx),
 6430            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6431        );
 6432    });
 6433}
 6434
 6435#[gpui::test]
 6436async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6437    init_test(cx, |_| {});
 6438
 6439    let (text, insertion_ranges) = marked_text_ranges(
 6440        indoc! {"
 6441            a.ˇ b
 6442            a.ˇ b
 6443            a.ˇ b
 6444        "},
 6445        false,
 6446    );
 6447
 6448    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6449    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6450
 6451    editor.update(cx, |editor, cx| {
 6452        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6453
 6454        editor
 6455            .insert_snippet(&insertion_ranges, snippet, cx)
 6456            .unwrap();
 6457
 6458        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6459            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6460            assert_eq!(editor.text(cx), expected_text);
 6461            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6462        }
 6463
 6464        assert(
 6465            editor,
 6466            cx,
 6467            indoc! {"
 6468                a.f(«one», two, «three») b
 6469                a.f(«one», two, «three») b
 6470                a.f(«one», two, «three») b
 6471            "},
 6472        );
 6473
 6474        // Can't move earlier than the first tab stop
 6475        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6476        assert(
 6477            editor,
 6478            cx,
 6479            indoc! {"
 6480                a.f(«one», two, «three») b
 6481                a.f(«one», two, «three») b
 6482                a.f(«one», two, «three») b
 6483            "},
 6484        );
 6485
 6486        assert!(editor.move_to_next_snippet_tabstop(cx));
 6487        assert(
 6488            editor,
 6489            cx,
 6490            indoc! {"
 6491                a.f(one, «two», three) b
 6492                a.f(one, «two», three) b
 6493                a.f(one, «two», three) b
 6494            "},
 6495        );
 6496
 6497        editor.move_to_prev_snippet_tabstop(cx);
 6498        assert(
 6499            editor,
 6500            cx,
 6501            indoc! {"
 6502                a.f(«one», two, «three») b
 6503                a.f(«one», two, «three») b
 6504                a.f(«one», two, «three») b
 6505            "},
 6506        );
 6507
 6508        assert!(editor.move_to_next_snippet_tabstop(cx));
 6509        assert(
 6510            editor,
 6511            cx,
 6512            indoc! {"
 6513                a.f(one, «two», three) b
 6514                a.f(one, «two», three) b
 6515                a.f(one, «two», three) b
 6516            "},
 6517        );
 6518        assert!(editor.move_to_next_snippet_tabstop(cx));
 6519        assert(
 6520            editor,
 6521            cx,
 6522            indoc! {"
 6523                a.f(one, two, three)ˇ b
 6524                a.f(one, two, three)ˇ b
 6525                a.f(one, two, three)ˇ b
 6526            "},
 6527        );
 6528
 6529        // As soon as the last tab stop is reached, snippet state is gone
 6530        editor.move_to_prev_snippet_tabstop(cx);
 6531        assert(
 6532            editor,
 6533            cx,
 6534            indoc! {"
 6535                a.f(one, two, three)ˇ b
 6536                a.f(one, two, three)ˇ b
 6537                a.f(one, two, three)ˇ b
 6538            "},
 6539        );
 6540    });
 6541}
 6542
 6543#[gpui::test]
 6544async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6545    init_test(cx, |_| {});
 6546
 6547    let fs = FakeFs::new(cx.executor());
 6548    fs.insert_file("/file.rs", Default::default()).await;
 6549
 6550    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6551
 6552    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6553    language_registry.add(rust_lang());
 6554    let mut fake_servers = language_registry.register_fake_lsp(
 6555        "Rust",
 6556        FakeLspAdapter {
 6557            capabilities: lsp::ServerCapabilities {
 6558                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6559                ..Default::default()
 6560            },
 6561            ..Default::default()
 6562        },
 6563    );
 6564
 6565    let buffer = project
 6566        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6567        .await
 6568        .unwrap();
 6569
 6570    cx.executor().start_waiting();
 6571    let fake_server = fake_servers.next().await.unwrap();
 6572
 6573    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6574    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6575    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6576    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6577
 6578    let save = editor
 6579        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6580        .unwrap();
 6581    fake_server
 6582        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6583            assert_eq!(
 6584                params.text_document.uri,
 6585                lsp::Url::from_file_path("/file.rs").unwrap()
 6586            );
 6587            assert_eq!(params.options.tab_size, 4);
 6588            Ok(Some(vec![lsp::TextEdit::new(
 6589                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6590                ", ".to_string(),
 6591            )]))
 6592        })
 6593        .next()
 6594        .await;
 6595    cx.executor().start_waiting();
 6596    save.await;
 6597
 6598    assert_eq!(
 6599        editor.update(cx, |editor, cx| editor.text(cx)),
 6600        "one, two\nthree\n"
 6601    );
 6602    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6603
 6604    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6605    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6606
 6607    // Ensure we can still save even if formatting hangs.
 6608    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6609        assert_eq!(
 6610            params.text_document.uri,
 6611            lsp::Url::from_file_path("/file.rs").unwrap()
 6612        );
 6613        futures::future::pending::<()>().await;
 6614        unreachable!()
 6615    });
 6616    let save = editor
 6617        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6618        .unwrap();
 6619    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6620    cx.executor().start_waiting();
 6621    save.await;
 6622    assert_eq!(
 6623        editor.update(cx, |editor, cx| editor.text(cx)),
 6624        "one\ntwo\nthree\n"
 6625    );
 6626    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6627
 6628    // For non-dirty buffer, no formatting request should be sent
 6629    let save = editor
 6630        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6631        .unwrap();
 6632    let _pending_format_request = fake_server
 6633        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6634            panic!("Should not be invoked on non-dirty buffer");
 6635        })
 6636        .next();
 6637    cx.executor().start_waiting();
 6638    save.await;
 6639
 6640    // Set rust language override and assert overridden tabsize is sent to language server
 6641    update_test_language_settings(cx, |settings| {
 6642        settings.languages.insert(
 6643            "Rust".into(),
 6644            LanguageSettingsContent {
 6645                tab_size: NonZeroU32::new(8),
 6646                ..Default::default()
 6647            },
 6648        );
 6649    });
 6650
 6651    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6652    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6653    let save = editor
 6654        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6655        .unwrap();
 6656    fake_server
 6657        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6658            assert_eq!(
 6659                params.text_document.uri,
 6660                lsp::Url::from_file_path("/file.rs").unwrap()
 6661            );
 6662            assert_eq!(params.options.tab_size, 8);
 6663            Ok(Some(vec![]))
 6664        })
 6665        .next()
 6666        .await;
 6667    cx.executor().start_waiting();
 6668    save.await;
 6669}
 6670
 6671#[gpui::test]
 6672async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6673    init_test(cx, |_| {});
 6674
 6675    let cols = 4;
 6676    let rows = 10;
 6677    let sample_text_1 = sample_text(rows, cols, 'a');
 6678    assert_eq!(
 6679        sample_text_1,
 6680        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6681    );
 6682    let sample_text_2 = sample_text(rows, cols, 'l');
 6683    assert_eq!(
 6684        sample_text_2,
 6685        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6686    );
 6687    let sample_text_3 = sample_text(rows, cols, 'v');
 6688    assert_eq!(
 6689        sample_text_3,
 6690        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6691    );
 6692
 6693    let fs = FakeFs::new(cx.executor());
 6694    fs.insert_tree(
 6695        "/a",
 6696        json!({
 6697            "main.rs": sample_text_1,
 6698            "other.rs": sample_text_2,
 6699            "lib.rs": sample_text_3,
 6700        }),
 6701    )
 6702    .await;
 6703
 6704    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6705    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6706    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6707
 6708    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6709    language_registry.add(rust_lang());
 6710    let mut fake_servers = language_registry.register_fake_lsp(
 6711        "Rust",
 6712        FakeLspAdapter {
 6713            capabilities: lsp::ServerCapabilities {
 6714                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6715                ..Default::default()
 6716            },
 6717            ..Default::default()
 6718        },
 6719    );
 6720
 6721    let worktree = project.update(cx, |project, cx| {
 6722        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6723        assert_eq!(worktrees.len(), 1);
 6724        worktrees.pop().unwrap()
 6725    });
 6726    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6727
 6728    let buffer_1 = project
 6729        .update(cx, |project, cx| {
 6730            project.open_buffer((worktree_id, "main.rs"), cx)
 6731        })
 6732        .await
 6733        .unwrap();
 6734    let buffer_2 = project
 6735        .update(cx, |project, cx| {
 6736            project.open_buffer((worktree_id, "other.rs"), cx)
 6737        })
 6738        .await
 6739        .unwrap();
 6740    let buffer_3 = project
 6741        .update(cx, |project, cx| {
 6742            project.open_buffer((worktree_id, "lib.rs"), cx)
 6743        })
 6744        .await
 6745        .unwrap();
 6746
 6747    let multi_buffer = cx.new_model(|cx| {
 6748        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6749        multi_buffer.push_excerpts(
 6750            buffer_1.clone(),
 6751            [
 6752                ExcerptRange {
 6753                    context: Point::new(0, 0)..Point::new(3, 0),
 6754                    primary: None,
 6755                },
 6756                ExcerptRange {
 6757                    context: Point::new(5, 0)..Point::new(7, 0),
 6758                    primary: None,
 6759                },
 6760                ExcerptRange {
 6761                    context: Point::new(9, 0)..Point::new(10, 4),
 6762                    primary: None,
 6763                },
 6764            ],
 6765            cx,
 6766        );
 6767        multi_buffer.push_excerpts(
 6768            buffer_2.clone(),
 6769            [
 6770                ExcerptRange {
 6771                    context: Point::new(0, 0)..Point::new(3, 0),
 6772                    primary: None,
 6773                },
 6774                ExcerptRange {
 6775                    context: Point::new(5, 0)..Point::new(7, 0),
 6776                    primary: None,
 6777                },
 6778                ExcerptRange {
 6779                    context: Point::new(9, 0)..Point::new(10, 4),
 6780                    primary: None,
 6781                },
 6782            ],
 6783            cx,
 6784        );
 6785        multi_buffer.push_excerpts(
 6786            buffer_3.clone(),
 6787            [
 6788                ExcerptRange {
 6789                    context: Point::new(0, 0)..Point::new(3, 0),
 6790                    primary: None,
 6791                },
 6792                ExcerptRange {
 6793                    context: Point::new(5, 0)..Point::new(7, 0),
 6794                    primary: None,
 6795                },
 6796                ExcerptRange {
 6797                    context: Point::new(9, 0)..Point::new(10, 4),
 6798                    primary: None,
 6799                },
 6800            ],
 6801            cx,
 6802        );
 6803        multi_buffer
 6804    });
 6805    let multi_buffer_editor = cx.new_view(|cx| {
 6806        Editor::new(
 6807            EditorMode::Full,
 6808            multi_buffer,
 6809            Some(project.clone()),
 6810            true,
 6811            cx,
 6812        )
 6813    });
 6814
 6815    multi_buffer_editor.update(cx, |editor, cx| {
 6816        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6817        editor.insert("|one|two|three|", cx);
 6818    });
 6819    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6820    multi_buffer_editor.update(cx, |editor, cx| {
 6821        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6822            s.select_ranges(Some(60..70))
 6823        });
 6824        editor.insert("|four|five|six|", cx);
 6825    });
 6826    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6827
 6828    // First two buffers should be edited, but not the third one.
 6829    assert_eq!(
 6830        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6831        "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}",
 6832    );
 6833    buffer_1.update(cx, |buffer, _| {
 6834        assert!(buffer.is_dirty());
 6835        assert_eq!(
 6836            buffer.text(),
 6837            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6838        )
 6839    });
 6840    buffer_2.update(cx, |buffer, _| {
 6841        assert!(buffer.is_dirty());
 6842        assert_eq!(
 6843            buffer.text(),
 6844            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6845        )
 6846    });
 6847    buffer_3.update(cx, |buffer, _| {
 6848        assert!(!buffer.is_dirty());
 6849        assert_eq!(buffer.text(), sample_text_3,)
 6850    });
 6851
 6852    cx.executor().start_waiting();
 6853    let save = multi_buffer_editor
 6854        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6855        .unwrap();
 6856
 6857    let fake_server = fake_servers.next().await.unwrap();
 6858    fake_server
 6859        .server
 6860        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6861            Ok(Some(vec![lsp::TextEdit::new(
 6862                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6863                format!("[{} formatted]", params.text_document.uri),
 6864            )]))
 6865        })
 6866        .detach();
 6867    save.await;
 6868
 6869    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6870    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6871    assert_eq!(
 6872        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6873        "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}",
 6874    );
 6875    buffer_1.update(cx, |buffer, _| {
 6876        assert!(!buffer.is_dirty());
 6877        assert_eq!(
 6878            buffer.text(),
 6879            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6880        )
 6881    });
 6882    buffer_2.update(cx, |buffer, _| {
 6883        assert!(!buffer.is_dirty());
 6884        assert_eq!(
 6885            buffer.text(),
 6886            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6887        )
 6888    });
 6889    buffer_3.update(cx, |buffer, _| {
 6890        assert!(!buffer.is_dirty());
 6891        assert_eq!(buffer.text(), sample_text_3,)
 6892    });
 6893}
 6894
 6895#[gpui::test]
 6896async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6897    init_test(cx, |_| {});
 6898
 6899    let fs = FakeFs::new(cx.executor());
 6900    fs.insert_file("/file.rs", Default::default()).await;
 6901
 6902    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6903
 6904    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6905    language_registry.add(rust_lang());
 6906    let mut fake_servers = language_registry.register_fake_lsp(
 6907        "Rust",
 6908        FakeLspAdapter {
 6909            capabilities: lsp::ServerCapabilities {
 6910                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6911                ..Default::default()
 6912            },
 6913            ..Default::default()
 6914        },
 6915    );
 6916
 6917    let buffer = project
 6918        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6919        .await
 6920        .unwrap();
 6921
 6922    cx.executor().start_waiting();
 6923    let fake_server = fake_servers.next().await.unwrap();
 6924
 6925    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6926    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6927    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6928    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6929
 6930    let save = editor
 6931        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6932        .unwrap();
 6933    fake_server
 6934        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6935            assert_eq!(
 6936                params.text_document.uri,
 6937                lsp::Url::from_file_path("/file.rs").unwrap()
 6938            );
 6939            assert_eq!(params.options.tab_size, 4);
 6940            Ok(Some(vec![lsp::TextEdit::new(
 6941                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6942                ", ".to_string(),
 6943            )]))
 6944        })
 6945        .next()
 6946        .await;
 6947    cx.executor().start_waiting();
 6948    save.await;
 6949    assert_eq!(
 6950        editor.update(cx, |editor, cx| editor.text(cx)),
 6951        "one, two\nthree\n"
 6952    );
 6953    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6954
 6955    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6956    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6957
 6958    // Ensure we can still save even if formatting hangs.
 6959    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6960        move |params, _| async move {
 6961            assert_eq!(
 6962                params.text_document.uri,
 6963                lsp::Url::from_file_path("/file.rs").unwrap()
 6964            );
 6965            futures::future::pending::<()>().await;
 6966            unreachable!()
 6967        },
 6968    );
 6969    let save = editor
 6970        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6971        .unwrap();
 6972    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6973    cx.executor().start_waiting();
 6974    save.await;
 6975    assert_eq!(
 6976        editor.update(cx, |editor, cx| editor.text(cx)),
 6977        "one\ntwo\nthree\n"
 6978    );
 6979    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6980
 6981    // For non-dirty buffer, no formatting request should be sent
 6982    let save = editor
 6983        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6984        .unwrap();
 6985    let _pending_format_request = fake_server
 6986        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6987            panic!("Should not be invoked on non-dirty buffer");
 6988        })
 6989        .next();
 6990    cx.executor().start_waiting();
 6991    save.await;
 6992
 6993    // Set Rust language override and assert overridden tabsize is sent to language server
 6994    update_test_language_settings(cx, |settings| {
 6995        settings.languages.insert(
 6996            "Rust".into(),
 6997            LanguageSettingsContent {
 6998                tab_size: NonZeroU32::new(8),
 6999                ..Default::default()
 7000            },
 7001        );
 7002    });
 7003
 7004    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7005    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7006    let save = editor
 7007        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7008        .unwrap();
 7009    fake_server
 7010        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7011            assert_eq!(
 7012                params.text_document.uri,
 7013                lsp::Url::from_file_path("/file.rs").unwrap()
 7014            );
 7015            assert_eq!(params.options.tab_size, 8);
 7016            Ok(Some(vec![]))
 7017        })
 7018        .next()
 7019        .await;
 7020    cx.executor().start_waiting();
 7021    save.await;
 7022}
 7023
 7024#[gpui::test]
 7025async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7026    init_test(cx, |settings| {
 7027        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7028            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7029        ))
 7030    });
 7031
 7032    let fs = FakeFs::new(cx.executor());
 7033    fs.insert_file("/file.rs", Default::default()).await;
 7034
 7035    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7036
 7037    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7038    language_registry.add(Arc::new(Language::new(
 7039        LanguageConfig {
 7040            name: "Rust".into(),
 7041            matcher: LanguageMatcher {
 7042                path_suffixes: vec!["rs".to_string()],
 7043                ..Default::default()
 7044            },
 7045            ..LanguageConfig::default()
 7046        },
 7047        Some(tree_sitter_rust::LANGUAGE.into()),
 7048    )));
 7049    update_test_language_settings(cx, |settings| {
 7050        // Enable Prettier formatting for the same buffer, and ensure
 7051        // LSP is called instead of Prettier.
 7052        settings.defaults.prettier = Some(PrettierSettings {
 7053            allowed: true,
 7054            ..PrettierSettings::default()
 7055        });
 7056    });
 7057    let mut fake_servers = language_registry.register_fake_lsp(
 7058        "Rust",
 7059        FakeLspAdapter {
 7060            capabilities: lsp::ServerCapabilities {
 7061                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7062                ..Default::default()
 7063            },
 7064            ..Default::default()
 7065        },
 7066    );
 7067
 7068    let buffer = project
 7069        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7070        .await
 7071        .unwrap();
 7072
 7073    cx.executor().start_waiting();
 7074    let fake_server = fake_servers.next().await.unwrap();
 7075
 7076    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7077    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7078    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7079
 7080    let format = editor
 7081        .update(cx, |editor, cx| {
 7082            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 7083        })
 7084        .unwrap();
 7085    fake_server
 7086        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7087            assert_eq!(
 7088                params.text_document.uri,
 7089                lsp::Url::from_file_path("/file.rs").unwrap()
 7090            );
 7091            assert_eq!(params.options.tab_size, 4);
 7092            Ok(Some(vec![lsp::TextEdit::new(
 7093                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7094                ", ".to_string(),
 7095            )]))
 7096        })
 7097        .next()
 7098        .await;
 7099    cx.executor().start_waiting();
 7100    format.await;
 7101    assert_eq!(
 7102        editor.update(cx, |editor, cx| editor.text(cx)),
 7103        "one, two\nthree\n"
 7104    );
 7105
 7106    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7107    // Ensure we don't lock if formatting hangs.
 7108    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7109        assert_eq!(
 7110            params.text_document.uri,
 7111            lsp::Url::from_file_path("/file.rs").unwrap()
 7112        );
 7113        futures::future::pending::<()>().await;
 7114        unreachable!()
 7115    });
 7116    let format = editor
 7117        .update(cx, |editor, cx| {
 7118            editor.perform_format(project, FormatTrigger::Manual, cx)
 7119        })
 7120        .unwrap();
 7121    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7122    cx.executor().start_waiting();
 7123    format.await;
 7124    assert_eq!(
 7125        editor.update(cx, |editor, cx| editor.text(cx)),
 7126        "one\ntwo\nthree\n"
 7127    );
 7128}
 7129
 7130#[gpui::test]
 7131async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7132    init_test(cx, |_| {});
 7133
 7134    let mut cx = EditorLspTestContext::new_rust(
 7135        lsp::ServerCapabilities {
 7136            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7137            ..Default::default()
 7138        },
 7139        cx,
 7140    )
 7141    .await;
 7142
 7143    cx.set_state(indoc! {"
 7144        one.twoˇ
 7145    "});
 7146
 7147    // The format request takes a long time. When it completes, it inserts
 7148    // a newline and an indent before the `.`
 7149    cx.lsp
 7150        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7151            let executor = cx.background_executor().clone();
 7152            async move {
 7153                executor.timer(Duration::from_millis(100)).await;
 7154                Ok(Some(vec![lsp::TextEdit {
 7155                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7156                    new_text: "\n    ".into(),
 7157                }]))
 7158            }
 7159        });
 7160
 7161    // Submit a format request.
 7162    let format_1 = cx
 7163        .update_editor(|editor, cx| editor.format(&Format, cx))
 7164        .unwrap();
 7165    cx.executor().run_until_parked();
 7166
 7167    // Submit a second format request.
 7168    let format_2 = cx
 7169        .update_editor(|editor, cx| editor.format(&Format, cx))
 7170        .unwrap();
 7171    cx.executor().run_until_parked();
 7172
 7173    // Wait for both format requests to complete
 7174    cx.executor().advance_clock(Duration::from_millis(200));
 7175    cx.executor().start_waiting();
 7176    format_1.await.unwrap();
 7177    cx.executor().start_waiting();
 7178    format_2.await.unwrap();
 7179
 7180    // The formatting edits only happens once.
 7181    cx.assert_editor_state(indoc! {"
 7182        one
 7183            .twoˇ
 7184    "});
 7185}
 7186
 7187#[gpui::test]
 7188async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7189    init_test(cx, |settings| {
 7190        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7191    });
 7192
 7193    let mut cx = EditorLspTestContext::new_rust(
 7194        lsp::ServerCapabilities {
 7195            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7196            ..Default::default()
 7197        },
 7198        cx,
 7199    )
 7200    .await;
 7201
 7202    // Set up a buffer white some trailing whitespace and no trailing newline.
 7203    cx.set_state(
 7204        &[
 7205            "one ",   //
 7206            "twoˇ",   //
 7207            "three ", //
 7208            "four",   //
 7209        ]
 7210        .join("\n"),
 7211    );
 7212
 7213    // Submit a format request.
 7214    let format = cx
 7215        .update_editor(|editor, cx| editor.format(&Format, cx))
 7216        .unwrap();
 7217
 7218    // Record which buffer changes have been sent to the language server
 7219    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7220    cx.lsp
 7221        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7222            let buffer_changes = buffer_changes.clone();
 7223            move |params, _| {
 7224                buffer_changes.lock().extend(
 7225                    params
 7226                        .content_changes
 7227                        .into_iter()
 7228                        .map(|e| (e.range.unwrap(), e.text)),
 7229                );
 7230            }
 7231        });
 7232
 7233    // Handle formatting requests to the language server.
 7234    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7235        let buffer_changes = buffer_changes.clone();
 7236        move |_, _| {
 7237            // When formatting is requested, trailing whitespace has already been stripped,
 7238            // and the trailing newline has already been added.
 7239            assert_eq!(
 7240                &buffer_changes.lock()[1..],
 7241                &[
 7242                    (
 7243                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7244                        "".into()
 7245                    ),
 7246                    (
 7247                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7248                        "".into()
 7249                    ),
 7250                    (
 7251                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7252                        "\n".into()
 7253                    ),
 7254                ]
 7255            );
 7256
 7257            // Insert blank lines between each line of the buffer.
 7258            async move {
 7259                Ok(Some(vec![
 7260                    lsp::TextEdit {
 7261                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7262                        new_text: "\n".into(),
 7263                    },
 7264                    lsp::TextEdit {
 7265                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7266                        new_text: "\n".into(),
 7267                    },
 7268                ]))
 7269            }
 7270        }
 7271    });
 7272
 7273    // After formatting the buffer, the trailing whitespace is stripped,
 7274    // a newline is appended, and the edits provided by the language server
 7275    // have been applied.
 7276    format.await.unwrap();
 7277    cx.assert_editor_state(
 7278        &[
 7279            "one",   //
 7280            "",      //
 7281            "twoˇ",  //
 7282            "",      //
 7283            "three", //
 7284            "four",  //
 7285            "",      //
 7286        ]
 7287        .join("\n"),
 7288    );
 7289
 7290    // Undoing the formatting undoes the trailing whitespace removal, the
 7291    // trailing newline, and the LSP edits.
 7292    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7293    cx.assert_editor_state(
 7294        &[
 7295            "one ",   //
 7296            "twoˇ",   //
 7297            "three ", //
 7298            "four",   //
 7299        ]
 7300        .join("\n"),
 7301    );
 7302}
 7303
 7304#[gpui::test]
 7305async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7306    cx: &mut gpui::TestAppContext,
 7307) {
 7308    init_test(cx, |_| {});
 7309
 7310    cx.update(|cx| {
 7311        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7312            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7313                settings.auto_signature_help = Some(true);
 7314            });
 7315        });
 7316    });
 7317
 7318    let mut cx = EditorLspTestContext::new_rust(
 7319        lsp::ServerCapabilities {
 7320            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7321                ..Default::default()
 7322            }),
 7323            ..Default::default()
 7324        },
 7325        cx,
 7326    )
 7327    .await;
 7328
 7329    let language = Language::new(
 7330        LanguageConfig {
 7331            name: "Rust".into(),
 7332            brackets: BracketPairConfig {
 7333                pairs: vec![
 7334                    BracketPair {
 7335                        start: "{".to_string(),
 7336                        end: "}".to_string(),
 7337                        close: true,
 7338                        surround: true,
 7339                        newline: true,
 7340                    },
 7341                    BracketPair {
 7342                        start: "(".to_string(),
 7343                        end: ")".to_string(),
 7344                        close: true,
 7345                        surround: true,
 7346                        newline: true,
 7347                    },
 7348                    BracketPair {
 7349                        start: "/*".to_string(),
 7350                        end: " */".to_string(),
 7351                        close: true,
 7352                        surround: true,
 7353                        newline: true,
 7354                    },
 7355                    BracketPair {
 7356                        start: "[".to_string(),
 7357                        end: "]".to_string(),
 7358                        close: false,
 7359                        surround: false,
 7360                        newline: true,
 7361                    },
 7362                    BracketPair {
 7363                        start: "\"".to_string(),
 7364                        end: "\"".to_string(),
 7365                        close: true,
 7366                        surround: true,
 7367                        newline: false,
 7368                    },
 7369                    BracketPair {
 7370                        start: "<".to_string(),
 7371                        end: ">".to_string(),
 7372                        close: false,
 7373                        surround: true,
 7374                        newline: true,
 7375                    },
 7376                ],
 7377                ..Default::default()
 7378            },
 7379            autoclose_before: "})]".to_string(),
 7380            ..Default::default()
 7381        },
 7382        Some(tree_sitter_rust::LANGUAGE.into()),
 7383    );
 7384    let language = Arc::new(language);
 7385
 7386    cx.language_registry().add(language.clone());
 7387    cx.update_buffer(|buffer, cx| {
 7388        buffer.set_language(Some(language), cx);
 7389    });
 7390
 7391    cx.set_state(
 7392        &r#"
 7393            fn main() {
 7394                sampleˇ
 7395            }
 7396        "#
 7397        .unindent(),
 7398    );
 7399
 7400    cx.update_editor(|view, cx| {
 7401        view.handle_input("(", cx);
 7402    });
 7403    cx.assert_editor_state(
 7404        &"
 7405            fn main() {
 7406                sample(ˇ)
 7407            }
 7408        "
 7409        .unindent(),
 7410    );
 7411
 7412    let mocked_response = lsp::SignatureHelp {
 7413        signatures: vec![lsp::SignatureInformation {
 7414            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7415            documentation: None,
 7416            parameters: Some(vec![
 7417                lsp::ParameterInformation {
 7418                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7419                    documentation: None,
 7420                },
 7421                lsp::ParameterInformation {
 7422                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7423                    documentation: None,
 7424                },
 7425            ]),
 7426            active_parameter: None,
 7427        }],
 7428        active_signature: Some(0),
 7429        active_parameter: Some(0),
 7430    };
 7431    handle_signature_help_request(&mut cx, mocked_response).await;
 7432
 7433    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7434        .await;
 7435
 7436    cx.editor(|editor, _| {
 7437        let signature_help_state = editor.signature_help_state.popover().cloned();
 7438        assert!(signature_help_state.is_some());
 7439        let ParsedMarkdown {
 7440            text, highlights, ..
 7441        } = signature_help_state.unwrap().parsed_content;
 7442        assert_eq!(text, "param1: u8, param2: u8");
 7443        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7444    });
 7445}
 7446
 7447#[gpui::test]
 7448async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7449    init_test(cx, |_| {});
 7450
 7451    cx.update(|cx| {
 7452        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7453            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7454                settings.auto_signature_help = Some(false);
 7455                settings.show_signature_help_after_edits = Some(false);
 7456            });
 7457        });
 7458    });
 7459
 7460    let mut cx = EditorLspTestContext::new_rust(
 7461        lsp::ServerCapabilities {
 7462            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7463                ..Default::default()
 7464            }),
 7465            ..Default::default()
 7466        },
 7467        cx,
 7468    )
 7469    .await;
 7470
 7471    let language = Language::new(
 7472        LanguageConfig {
 7473            name: "Rust".into(),
 7474            brackets: BracketPairConfig {
 7475                pairs: vec![
 7476                    BracketPair {
 7477                        start: "{".to_string(),
 7478                        end: "}".to_string(),
 7479                        close: true,
 7480                        surround: true,
 7481                        newline: true,
 7482                    },
 7483                    BracketPair {
 7484                        start: "(".to_string(),
 7485                        end: ")".to_string(),
 7486                        close: true,
 7487                        surround: true,
 7488                        newline: true,
 7489                    },
 7490                    BracketPair {
 7491                        start: "/*".to_string(),
 7492                        end: " */".to_string(),
 7493                        close: true,
 7494                        surround: true,
 7495                        newline: true,
 7496                    },
 7497                    BracketPair {
 7498                        start: "[".to_string(),
 7499                        end: "]".to_string(),
 7500                        close: false,
 7501                        surround: false,
 7502                        newline: true,
 7503                    },
 7504                    BracketPair {
 7505                        start: "\"".to_string(),
 7506                        end: "\"".to_string(),
 7507                        close: true,
 7508                        surround: true,
 7509                        newline: false,
 7510                    },
 7511                    BracketPair {
 7512                        start: "<".to_string(),
 7513                        end: ">".to_string(),
 7514                        close: false,
 7515                        surround: true,
 7516                        newline: true,
 7517                    },
 7518                ],
 7519                ..Default::default()
 7520            },
 7521            autoclose_before: "})]".to_string(),
 7522            ..Default::default()
 7523        },
 7524        Some(tree_sitter_rust::LANGUAGE.into()),
 7525    );
 7526    let language = Arc::new(language);
 7527
 7528    cx.language_registry().add(language.clone());
 7529    cx.update_buffer(|buffer, cx| {
 7530        buffer.set_language(Some(language), cx);
 7531    });
 7532
 7533    // Ensure that signature_help is not called when no signature help is enabled.
 7534    cx.set_state(
 7535        &r#"
 7536            fn main() {
 7537                sampleˇ
 7538            }
 7539        "#
 7540        .unindent(),
 7541    );
 7542    cx.update_editor(|view, cx| {
 7543        view.handle_input("(", cx);
 7544    });
 7545    cx.assert_editor_state(
 7546        &"
 7547            fn main() {
 7548                sample(ˇ)
 7549            }
 7550        "
 7551        .unindent(),
 7552    );
 7553    cx.editor(|editor, _| {
 7554        assert!(editor.signature_help_state.task().is_none());
 7555    });
 7556
 7557    let mocked_response = lsp::SignatureHelp {
 7558        signatures: vec![lsp::SignatureInformation {
 7559            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7560            documentation: None,
 7561            parameters: Some(vec![
 7562                lsp::ParameterInformation {
 7563                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7564                    documentation: None,
 7565                },
 7566                lsp::ParameterInformation {
 7567                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7568                    documentation: None,
 7569                },
 7570            ]),
 7571            active_parameter: None,
 7572        }],
 7573        active_signature: Some(0),
 7574        active_parameter: Some(0),
 7575    };
 7576
 7577    // Ensure that signature_help is called when enabled afte edits
 7578    cx.update(|cx| {
 7579        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7580            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7581                settings.auto_signature_help = Some(false);
 7582                settings.show_signature_help_after_edits = Some(true);
 7583            });
 7584        });
 7585    });
 7586    cx.set_state(
 7587        &r#"
 7588            fn main() {
 7589                sampleˇ
 7590            }
 7591        "#
 7592        .unindent(),
 7593    );
 7594    cx.update_editor(|view, cx| {
 7595        view.handle_input("(", cx);
 7596    });
 7597    cx.assert_editor_state(
 7598        &"
 7599            fn main() {
 7600                sample(ˇ)
 7601            }
 7602        "
 7603        .unindent(),
 7604    );
 7605    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7606    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7607        .await;
 7608    cx.update_editor(|editor, _| {
 7609        let signature_help_state = editor.signature_help_state.popover().cloned();
 7610        assert!(signature_help_state.is_some());
 7611        let ParsedMarkdown {
 7612            text, highlights, ..
 7613        } = signature_help_state.unwrap().parsed_content;
 7614        assert_eq!(text, "param1: u8, param2: u8");
 7615        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7616        editor.signature_help_state = SignatureHelpState::default();
 7617    });
 7618
 7619    // Ensure that signature_help is called when auto signature help override is enabled
 7620    cx.update(|cx| {
 7621        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7622            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7623                settings.auto_signature_help = Some(true);
 7624                settings.show_signature_help_after_edits = Some(false);
 7625            });
 7626        });
 7627    });
 7628    cx.set_state(
 7629        &r#"
 7630            fn main() {
 7631                sampleˇ
 7632            }
 7633        "#
 7634        .unindent(),
 7635    );
 7636    cx.update_editor(|view, cx| {
 7637        view.handle_input("(", cx);
 7638    });
 7639    cx.assert_editor_state(
 7640        &"
 7641            fn main() {
 7642                sample(ˇ)
 7643            }
 7644        "
 7645        .unindent(),
 7646    );
 7647    handle_signature_help_request(&mut cx, mocked_response).await;
 7648    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7649        .await;
 7650    cx.editor(|editor, _| {
 7651        let signature_help_state = editor.signature_help_state.popover().cloned();
 7652        assert!(signature_help_state.is_some());
 7653        let ParsedMarkdown {
 7654            text, highlights, ..
 7655        } = signature_help_state.unwrap().parsed_content;
 7656        assert_eq!(text, "param1: u8, param2: u8");
 7657        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7658    });
 7659}
 7660
 7661#[gpui::test]
 7662async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7663    init_test(cx, |_| {});
 7664    cx.update(|cx| {
 7665        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7666            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7667                settings.auto_signature_help = Some(true);
 7668            });
 7669        });
 7670    });
 7671
 7672    let mut cx = EditorLspTestContext::new_rust(
 7673        lsp::ServerCapabilities {
 7674            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7675                ..Default::default()
 7676            }),
 7677            ..Default::default()
 7678        },
 7679        cx,
 7680    )
 7681    .await;
 7682
 7683    // A test that directly calls `show_signature_help`
 7684    cx.update_editor(|editor, cx| {
 7685        editor.show_signature_help(&ShowSignatureHelp, cx);
 7686    });
 7687
 7688    let mocked_response = lsp::SignatureHelp {
 7689        signatures: vec![lsp::SignatureInformation {
 7690            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7691            documentation: None,
 7692            parameters: Some(vec![
 7693                lsp::ParameterInformation {
 7694                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7695                    documentation: None,
 7696                },
 7697                lsp::ParameterInformation {
 7698                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7699                    documentation: None,
 7700                },
 7701            ]),
 7702            active_parameter: None,
 7703        }],
 7704        active_signature: Some(0),
 7705        active_parameter: Some(0),
 7706    };
 7707    handle_signature_help_request(&mut cx, mocked_response).await;
 7708
 7709    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7710        .await;
 7711
 7712    cx.editor(|editor, _| {
 7713        let signature_help_state = editor.signature_help_state.popover().cloned();
 7714        assert!(signature_help_state.is_some());
 7715        let ParsedMarkdown {
 7716            text, highlights, ..
 7717        } = signature_help_state.unwrap().parsed_content;
 7718        assert_eq!(text, "param1: u8, param2: u8");
 7719        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7720    });
 7721
 7722    // When exiting outside from inside the brackets, `signature_help` is closed.
 7723    cx.set_state(indoc! {"
 7724        fn main() {
 7725            sample(ˇ);
 7726        }
 7727
 7728        fn sample(param1: u8, param2: u8) {}
 7729    "});
 7730
 7731    cx.update_editor(|editor, cx| {
 7732        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7733    });
 7734
 7735    let mocked_response = lsp::SignatureHelp {
 7736        signatures: Vec::new(),
 7737        active_signature: None,
 7738        active_parameter: None,
 7739    };
 7740    handle_signature_help_request(&mut cx, mocked_response).await;
 7741
 7742    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7743        .await;
 7744
 7745    cx.editor(|editor, _| {
 7746        assert!(!editor.signature_help_state.is_shown());
 7747    });
 7748
 7749    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7750    cx.set_state(indoc! {"
 7751        fn main() {
 7752            sample(ˇ);
 7753        }
 7754
 7755        fn sample(param1: u8, param2: u8) {}
 7756    "});
 7757
 7758    let mocked_response = lsp::SignatureHelp {
 7759        signatures: vec![lsp::SignatureInformation {
 7760            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7761            documentation: None,
 7762            parameters: Some(vec![
 7763                lsp::ParameterInformation {
 7764                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7765                    documentation: None,
 7766                },
 7767                lsp::ParameterInformation {
 7768                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7769                    documentation: None,
 7770                },
 7771            ]),
 7772            active_parameter: None,
 7773        }],
 7774        active_signature: Some(0),
 7775        active_parameter: Some(0),
 7776    };
 7777    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7778    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7779        .await;
 7780    cx.editor(|editor, _| {
 7781        assert!(editor.signature_help_state.is_shown());
 7782    });
 7783
 7784    // Restore the popover with more parameter input
 7785    cx.set_state(indoc! {"
 7786        fn main() {
 7787            sample(param1, param2ˇ);
 7788        }
 7789
 7790        fn sample(param1: u8, param2: u8) {}
 7791    "});
 7792
 7793    let mocked_response = lsp::SignatureHelp {
 7794        signatures: vec![lsp::SignatureInformation {
 7795            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7796            documentation: None,
 7797            parameters: Some(vec![
 7798                lsp::ParameterInformation {
 7799                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7800                    documentation: None,
 7801                },
 7802                lsp::ParameterInformation {
 7803                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7804                    documentation: None,
 7805                },
 7806            ]),
 7807            active_parameter: None,
 7808        }],
 7809        active_signature: Some(0),
 7810        active_parameter: Some(1),
 7811    };
 7812    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7813    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7814        .await;
 7815
 7816    // When selecting a range, the popover is gone.
 7817    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7818    cx.update_editor(|editor, cx| {
 7819        editor.change_selections(None, cx, |s| {
 7820            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7821        })
 7822    });
 7823    cx.assert_editor_state(indoc! {"
 7824        fn main() {
 7825            sample(param1, «ˇparam2»);
 7826        }
 7827
 7828        fn sample(param1: u8, param2: u8) {}
 7829    "});
 7830    cx.editor(|editor, _| {
 7831        assert!(!editor.signature_help_state.is_shown());
 7832    });
 7833
 7834    // When unselecting again, the popover is back if within the brackets.
 7835    cx.update_editor(|editor, cx| {
 7836        editor.change_selections(None, cx, |s| {
 7837            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7838        })
 7839    });
 7840    cx.assert_editor_state(indoc! {"
 7841        fn main() {
 7842            sample(param1, ˇparam2);
 7843        }
 7844
 7845        fn sample(param1: u8, param2: u8) {}
 7846    "});
 7847    handle_signature_help_request(&mut cx, mocked_response).await;
 7848    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7849        .await;
 7850    cx.editor(|editor, _| {
 7851        assert!(editor.signature_help_state.is_shown());
 7852    });
 7853
 7854    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7855    cx.update_editor(|editor, cx| {
 7856        editor.change_selections(None, cx, |s| {
 7857            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7858            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7859        })
 7860    });
 7861    cx.assert_editor_state(indoc! {"
 7862        fn main() {
 7863            sample(param1, ˇparam2);
 7864        }
 7865
 7866        fn sample(param1: u8, param2: u8) {}
 7867    "});
 7868
 7869    let mocked_response = lsp::SignatureHelp {
 7870        signatures: vec![lsp::SignatureInformation {
 7871            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7872            documentation: None,
 7873            parameters: Some(vec![
 7874                lsp::ParameterInformation {
 7875                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7876                    documentation: None,
 7877                },
 7878                lsp::ParameterInformation {
 7879                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7880                    documentation: None,
 7881                },
 7882            ]),
 7883            active_parameter: None,
 7884        }],
 7885        active_signature: Some(0),
 7886        active_parameter: Some(1),
 7887    };
 7888    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7889    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7890        .await;
 7891    cx.update_editor(|editor, cx| {
 7892        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7893    });
 7894    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7895        .await;
 7896    cx.update_editor(|editor, cx| {
 7897        editor.change_selections(None, cx, |s| {
 7898            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7899        })
 7900    });
 7901    cx.assert_editor_state(indoc! {"
 7902        fn main() {
 7903            sample(param1, «ˇparam2»);
 7904        }
 7905
 7906        fn sample(param1: u8, param2: u8) {}
 7907    "});
 7908    cx.update_editor(|editor, cx| {
 7909        editor.change_selections(None, cx, |s| {
 7910            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7911        })
 7912    });
 7913    cx.assert_editor_state(indoc! {"
 7914        fn main() {
 7915            sample(param1, ˇparam2);
 7916        }
 7917
 7918        fn sample(param1: u8, param2: u8) {}
 7919    "});
 7920    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7921        .await;
 7922}
 7923
 7924#[gpui::test]
 7925async fn test_completion(cx: &mut gpui::TestAppContext) {
 7926    init_test(cx, |_| {});
 7927
 7928    let mut cx = EditorLspTestContext::new_rust(
 7929        lsp::ServerCapabilities {
 7930            completion_provider: Some(lsp::CompletionOptions {
 7931                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7932                resolve_provider: Some(true),
 7933                ..Default::default()
 7934            }),
 7935            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7936            ..Default::default()
 7937        },
 7938        cx,
 7939    )
 7940    .await;
 7941    let counter = Arc::new(AtomicUsize::new(0));
 7942
 7943    cx.set_state(indoc! {"
 7944        oneˇ
 7945        two
 7946        three
 7947    "});
 7948    cx.simulate_keystroke(".");
 7949    handle_completion_request(
 7950        &mut cx,
 7951        indoc! {"
 7952            one.|<>
 7953            two
 7954            three
 7955        "},
 7956        vec!["first_completion", "second_completion"],
 7957        counter.clone(),
 7958    )
 7959    .await;
 7960    cx.condition(|editor, _| editor.context_menu_visible())
 7961        .await;
 7962    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7963
 7964    let _handler = handle_signature_help_request(
 7965        &mut cx,
 7966        lsp::SignatureHelp {
 7967            signatures: vec![lsp::SignatureInformation {
 7968                label: "test signature".to_string(),
 7969                documentation: None,
 7970                parameters: Some(vec![lsp::ParameterInformation {
 7971                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7972                    documentation: None,
 7973                }]),
 7974                active_parameter: None,
 7975            }],
 7976            active_signature: None,
 7977            active_parameter: None,
 7978        },
 7979    );
 7980    cx.update_editor(|editor, cx| {
 7981        assert!(
 7982            !editor.signature_help_state.is_shown(),
 7983            "No signature help was called for"
 7984        );
 7985        editor.show_signature_help(&ShowSignatureHelp, cx);
 7986    });
 7987    cx.run_until_parked();
 7988    cx.update_editor(|editor, _| {
 7989        assert!(
 7990            !editor.signature_help_state.is_shown(),
 7991            "No signature help should be shown when completions menu is open"
 7992        );
 7993    });
 7994
 7995    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7996        editor.context_menu_next(&Default::default(), cx);
 7997        editor
 7998            .confirm_completion(&ConfirmCompletion::default(), cx)
 7999            .unwrap()
 8000    });
 8001    cx.assert_editor_state(indoc! {"
 8002        one.second_completionˇ
 8003        two
 8004        three
 8005    "});
 8006
 8007    handle_resolve_completion_request(
 8008        &mut cx,
 8009        Some(vec![
 8010            (
 8011                //This overlaps with the primary completion edit which is
 8012                //misbehavior from the LSP spec, test that we filter it out
 8013                indoc! {"
 8014                    one.second_ˇcompletion
 8015                    two
 8016                    threeˇ
 8017                "},
 8018                "overlapping additional edit",
 8019            ),
 8020            (
 8021                indoc! {"
 8022                    one.second_completion
 8023                    two
 8024                    threeˇ
 8025                "},
 8026                "\nadditional edit",
 8027            ),
 8028        ]),
 8029    )
 8030    .await;
 8031    apply_additional_edits.await.unwrap();
 8032    cx.assert_editor_state(indoc! {"
 8033        one.second_completionˇ
 8034        two
 8035        three
 8036        additional edit
 8037    "});
 8038
 8039    cx.set_state(indoc! {"
 8040        one.second_completion
 8041        twoˇ
 8042        threeˇ
 8043        additional edit
 8044    "});
 8045    cx.simulate_keystroke(" ");
 8046    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8047    cx.simulate_keystroke("s");
 8048    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8049
 8050    cx.assert_editor_state(indoc! {"
 8051        one.second_completion
 8052        two sˇ
 8053        three sˇ
 8054        additional edit
 8055    "});
 8056    handle_completion_request(
 8057        &mut cx,
 8058        indoc! {"
 8059            one.second_completion
 8060            two s
 8061            three <s|>
 8062            additional edit
 8063        "},
 8064        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8065        counter.clone(),
 8066    )
 8067    .await;
 8068    cx.condition(|editor, _| editor.context_menu_visible())
 8069        .await;
 8070    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8071
 8072    cx.simulate_keystroke("i");
 8073
 8074    handle_completion_request(
 8075        &mut cx,
 8076        indoc! {"
 8077            one.second_completion
 8078            two si
 8079            three <si|>
 8080            additional edit
 8081        "},
 8082        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8083        counter.clone(),
 8084    )
 8085    .await;
 8086    cx.condition(|editor, _| editor.context_menu_visible())
 8087        .await;
 8088    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8089
 8090    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8091        editor
 8092            .confirm_completion(&ConfirmCompletion::default(), cx)
 8093            .unwrap()
 8094    });
 8095    cx.assert_editor_state(indoc! {"
 8096        one.second_completion
 8097        two sixth_completionˇ
 8098        three sixth_completionˇ
 8099        additional edit
 8100    "});
 8101
 8102    handle_resolve_completion_request(&mut cx, None).await;
 8103    apply_additional_edits.await.unwrap();
 8104
 8105    cx.update(|cx| {
 8106        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8107            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8108                settings.show_completions_on_input = Some(false);
 8109            });
 8110        })
 8111    });
 8112    cx.set_state("editorˇ");
 8113    cx.simulate_keystroke(".");
 8114    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8115    cx.simulate_keystroke("c");
 8116    cx.simulate_keystroke("l");
 8117    cx.simulate_keystroke("o");
 8118    cx.assert_editor_state("editor.cloˇ");
 8119    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8120    cx.update_editor(|editor, cx| {
 8121        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8122    });
 8123    handle_completion_request(
 8124        &mut cx,
 8125        "editor.<clo|>",
 8126        vec!["close", "clobber"],
 8127        counter.clone(),
 8128    )
 8129    .await;
 8130    cx.condition(|editor, _| editor.context_menu_visible())
 8131        .await;
 8132    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8133
 8134    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8135        editor
 8136            .confirm_completion(&ConfirmCompletion::default(), cx)
 8137            .unwrap()
 8138    });
 8139    cx.assert_editor_state("editor.closeˇ");
 8140    handle_resolve_completion_request(&mut cx, None).await;
 8141    apply_additional_edits.await.unwrap();
 8142}
 8143
 8144#[gpui::test]
 8145async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8146    init_test(cx, |_| {});
 8147    let mut cx = EditorLspTestContext::new_rust(
 8148        lsp::ServerCapabilities {
 8149            completion_provider: Some(lsp::CompletionOptions {
 8150                trigger_characters: Some(vec![".".to_string()]),
 8151                ..Default::default()
 8152            }),
 8153            ..Default::default()
 8154        },
 8155        cx,
 8156    )
 8157    .await;
 8158    cx.lsp
 8159        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8160            Ok(Some(lsp::CompletionResponse::Array(vec![
 8161                lsp::CompletionItem {
 8162                    label: "first".into(),
 8163                    ..Default::default()
 8164                },
 8165                lsp::CompletionItem {
 8166                    label: "last".into(),
 8167                    ..Default::default()
 8168                },
 8169            ])))
 8170        });
 8171    cx.set_state("variableˇ");
 8172    cx.simulate_keystroke(".");
 8173    cx.executor().run_until_parked();
 8174
 8175    cx.update_editor(|editor, _| {
 8176        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8177            assert_eq!(
 8178                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8179                &["first", "last"]
 8180            );
 8181        } else {
 8182            panic!("expected completion menu to be open");
 8183        }
 8184    });
 8185
 8186    cx.update_editor(|editor, cx| {
 8187        editor.move_page_down(&MovePageDown::default(), cx);
 8188        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8189            assert!(
 8190                menu.selected_item == 1,
 8191                "expected PageDown to select the last item from the context menu"
 8192            );
 8193        } else {
 8194            panic!("expected completion menu to stay open after PageDown");
 8195        }
 8196    });
 8197
 8198    cx.update_editor(|editor, cx| {
 8199        editor.move_page_up(&MovePageUp::default(), cx);
 8200        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8201            assert!(
 8202                menu.selected_item == 0,
 8203                "expected PageUp to select the first item from the context menu"
 8204            );
 8205        } else {
 8206            panic!("expected completion menu to stay open after PageUp");
 8207        }
 8208    });
 8209}
 8210
 8211#[gpui::test]
 8212async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8213    init_test(cx, |_| {});
 8214
 8215    let mut cx = EditorLspTestContext::new_rust(
 8216        lsp::ServerCapabilities {
 8217            completion_provider: Some(lsp::CompletionOptions {
 8218                trigger_characters: Some(vec![".".to_string()]),
 8219                resolve_provider: Some(true),
 8220                ..Default::default()
 8221            }),
 8222            ..Default::default()
 8223        },
 8224        cx,
 8225    )
 8226    .await;
 8227
 8228    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8229    cx.simulate_keystroke(".");
 8230    let completion_item = lsp::CompletionItem {
 8231        label: "Some".into(),
 8232        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8233        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8234        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8235            kind: lsp::MarkupKind::Markdown,
 8236            value: "```rust\nSome(2)\n```".to_string(),
 8237        })),
 8238        deprecated: Some(false),
 8239        sort_text: Some("Some".to_string()),
 8240        filter_text: Some("Some".to_string()),
 8241        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8242        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8243            range: lsp::Range {
 8244                start: lsp::Position {
 8245                    line: 0,
 8246                    character: 22,
 8247                },
 8248                end: lsp::Position {
 8249                    line: 0,
 8250                    character: 22,
 8251                },
 8252            },
 8253            new_text: "Some(2)".to_string(),
 8254        })),
 8255        additional_text_edits: Some(vec![lsp::TextEdit {
 8256            range: lsp::Range {
 8257                start: lsp::Position {
 8258                    line: 0,
 8259                    character: 20,
 8260                },
 8261                end: lsp::Position {
 8262                    line: 0,
 8263                    character: 22,
 8264                },
 8265            },
 8266            new_text: "".to_string(),
 8267        }]),
 8268        ..Default::default()
 8269    };
 8270
 8271    let closure_completion_item = completion_item.clone();
 8272    let counter = Arc::new(AtomicUsize::new(0));
 8273    let counter_clone = counter.clone();
 8274    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8275        let task_completion_item = closure_completion_item.clone();
 8276        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8277        async move {
 8278            Ok(Some(lsp::CompletionResponse::Array(vec![
 8279                task_completion_item,
 8280            ])))
 8281        }
 8282    });
 8283
 8284    cx.condition(|editor, _| editor.context_menu_visible())
 8285        .await;
 8286    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8287    assert!(request.next().await.is_some());
 8288    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8289
 8290    cx.simulate_keystroke("S");
 8291    cx.simulate_keystroke("o");
 8292    cx.simulate_keystroke("m");
 8293    cx.condition(|editor, _| editor.context_menu_visible())
 8294        .await;
 8295    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8296    assert!(request.next().await.is_some());
 8297    assert!(request.next().await.is_some());
 8298    assert!(request.next().await.is_some());
 8299    request.close();
 8300    assert!(request.next().await.is_none());
 8301    assert_eq!(
 8302        counter.load(atomic::Ordering::Acquire),
 8303        4,
 8304        "With the completions menu open, only one LSP request should happen per input"
 8305    );
 8306}
 8307
 8308#[gpui::test]
 8309async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8310    init_test(cx, |_| {});
 8311    let mut cx = EditorTestContext::new(cx).await;
 8312    let language = Arc::new(Language::new(
 8313        LanguageConfig {
 8314            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8315            ..Default::default()
 8316        },
 8317        Some(tree_sitter_rust::LANGUAGE.into()),
 8318    ));
 8319    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8320
 8321    // If multiple selections intersect a line, the line is only toggled once.
 8322    cx.set_state(indoc! {"
 8323        fn a() {
 8324            «//b();
 8325            ˇ»// «c();
 8326            //ˇ»  d();
 8327        }
 8328    "});
 8329
 8330    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8331
 8332    cx.assert_editor_state(indoc! {"
 8333        fn a() {
 8334            «b();
 8335            c();
 8336            ˇ» d();
 8337        }
 8338    "});
 8339
 8340    // The comment prefix is inserted at the same column for every line in a
 8341    // selection.
 8342    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8343
 8344    cx.assert_editor_state(indoc! {"
 8345        fn a() {
 8346            // «b();
 8347            // c();
 8348            ˇ»//  d();
 8349        }
 8350    "});
 8351
 8352    // If a selection ends at the beginning of a line, that line is not toggled.
 8353    cx.set_selections_state(indoc! {"
 8354        fn a() {
 8355            // b();
 8356            «// c();
 8357        ˇ»    //  d();
 8358        }
 8359    "});
 8360
 8361    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8362
 8363    cx.assert_editor_state(indoc! {"
 8364        fn a() {
 8365            // b();
 8366            «c();
 8367        ˇ»    //  d();
 8368        }
 8369    "});
 8370
 8371    // If a selection span a single line and is empty, the line is toggled.
 8372    cx.set_state(indoc! {"
 8373        fn a() {
 8374            a();
 8375            b();
 8376        ˇ
 8377        }
 8378    "});
 8379
 8380    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8381
 8382    cx.assert_editor_state(indoc! {"
 8383        fn a() {
 8384            a();
 8385            b();
 8386        //•ˇ
 8387        }
 8388    "});
 8389
 8390    // If a selection span multiple lines, empty lines are not toggled.
 8391    cx.set_state(indoc! {"
 8392        fn a() {
 8393            «a();
 8394
 8395            c();ˇ»
 8396        }
 8397    "});
 8398
 8399    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8400
 8401    cx.assert_editor_state(indoc! {"
 8402        fn a() {
 8403            // «a();
 8404
 8405            // c();ˇ»
 8406        }
 8407    "});
 8408
 8409    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8410    cx.set_state(indoc! {"
 8411        fn a() {
 8412            «// a();
 8413            /// b();
 8414            //! c();ˇ»
 8415        }
 8416    "});
 8417
 8418    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8419
 8420    cx.assert_editor_state(indoc! {"
 8421        fn a() {
 8422            «a();
 8423            b();
 8424            c();ˇ»
 8425        }
 8426    "});
 8427}
 8428
 8429#[gpui::test]
 8430async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8431    init_test(cx, |_| {});
 8432
 8433    let language = Arc::new(Language::new(
 8434        LanguageConfig {
 8435            line_comments: vec!["// ".into()],
 8436            ..Default::default()
 8437        },
 8438        Some(tree_sitter_rust::LANGUAGE.into()),
 8439    ));
 8440
 8441    let mut cx = EditorTestContext::new(cx).await;
 8442
 8443    cx.language_registry().add(language.clone());
 8444    cx.update_buffer(|buffer, cx| {
 8445        buffer.set_language(Some(language), cx);
 8446    });
 8447
 8448    let toggle_comments = &ToggleComments {
 8449        advance_downwards: true,
 8450    };
 8451
 8452    // Single cursor on one line -> advance
 8453    // Cursor moves horizontally 3 characters as well on non-blank line
 8454    cx.set_state(indoc!(
 8455        "fn a() {
 8456             ˇdog();
 8457             cat();
 8458        }"
 8459    ));
 8460    cx.update_editor(|editor, cx| {
 8461        editor.toggle_comments(toggle_comments, cx);
 8462    });
 8463    cx.assert_editor_state(indoc!(
 8464        "fn a() {
 8465             // dog();
 8466             catˇ();
 8467        }"
 8468    ));
 8469
 8470    // Single selection on one line -> don't advance
 8471    cx.set_state(indoc!(
 8472        "fn a() {
 8473             «dog()ˇ»;
 8474             cat();
 8475        }"
 8476    ));
 8477    cx.update_editor(|editor, cx| {
 8478        editor.toggle_comments(toggle_comments, cx);
 8479    });
 8480    cx.assert_editor_state(indoc!(
 8481        "fn a() {
 8482             // «dog()ˇ»;
 8483             cat();
 8484        }"
 8485    ));
 8486
 8487    // Multiple cursors on one line -> advance
 8488    cx.set_state(indoc!(
 8489        "fn a() {
 8490             ˇdˇog();
 8491             cat();
 8492        }"
 8493    ));
 8494    cx.update_editor(|editor, cx| {
 8495        editor.toggle_comments(toggle_comments, cx);
 8496    });
 8497    cx.assert_editor_state(indoc!(
 8498        "fn a() {
 8499             // dog();
 8500             catˇ(ˇ);
 8501        }"
 8502    ));
 8503
 8504    // Multiple cursors on one line, with selection -> don't advance
 8505    cx.set_state(indoc!(
 8506        "fn a() {
 8507             ˇdˇog«()ˇ»;
 8508             cat();
 8509        }"
 8510    ));
 8511    cx.update_editor(|editor, cx| {
 8512        editor.toggle_comments(toggle_comments, cx);
 8513    });
 8514    cx.assert_editor_state(indoc!(
 8515        "fn a() {
 8516             // ˇdˇog«()ˇ»;
 8517             cat();
 8518        }"
 8519    ));
 8520
 8521    // Single cursor on one line -> advance
 8522    // Cursor moves to column 0 on blank line
 8523    cx.set_state(indoc!(
 8524        "fn a() {
 8525             ˇdog();
 8526
 8527             cat();
 8528        }"
 8529    ));
 8530    cx.update_editor(|editor, cx| {
 8531        editor.toggle_comments(toggle_comments, cx);
 8532    });
 8533    cx.assert_editor_state(indoc!(
 8534        "fn a() {
 8535             // dog();
 8536        ˇ
 8537             cat();
 8538        }"
 8539    ));
 8540
 8541    // Single cursor on one line -> advance
 8542    // Cursor starts and ends at column 0
 8543    cx.set_state(indoc!(
 8544        "fn a() {
 8545         ˇ    dog();
 8546             cat();
 8547        }"
 8548    ));
 8549    cx.update_editor(|editor, cx| {
 8550        editor.toggle_comments(toggle_comments, cx);
 8551    });
 8552    cx.assert_editor_state(indoc!(
 8553        "fn a() {
 8554             // dog();
 8555         ˇ    cat();
 8556        }"
 8557    ));
 8558}
 8559
 8560#[gpui::test]
 8561async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8562    init_test(cx, |_| {});
 8563
 8564    let mut cx = EditorTestContext::new(cx).await;
 8565
 8566    let html_language = Arc::new(
 8567        Language::new(
 8568            LanguageConfig {
 8569                name: "HTML".into(),
 8570                block_comment: Some(("<!-- ".into(), " -->".into())),
 8571                ..Default::default()
 8572            },
 8573            Some(tree_sitter_html::language()),
 8574        )
 8575        .with_injection_query(
 8576            r#"
 8577            (script_element
 8578                (raw_text) @content
 8579                (#set! "language" "javascript"))
 8580            "#,
 8581        )
 8582        .unwrap(),
 8583    );
 8584
 8585    let javascript_language = Arc::new(Language::new(
 8586        LanguageConfig {
 8587            name: "JavaScript".into(),
 8588            line_comments: vec!["// ".into()],
 8589            ..Default::default()
 8590        },
 8591        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8592    ));
 8593
 8594    cx.language_registry().add(html_language.clone());
 8595    cx.language_registry().add(javascript_language.clone());
 8596    cx.update_buffer(|buffer, cx| {
 8597        buffer.set_language(Some(html_language), cx);
 8598    });
 8599
 8600    // Toggle comments for empty selections
 8601    cx.set_state(
 8602        &r#"
 8603            <p>A</p>ˇ
 8604            <p>B</p>ˇ
 8605            <p>C</p>ˇ
 8606        "#
 8607        .unindent(),
 8608    );
 8609    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8610    cx.assert_editor_state(
 8611        &r#"
 8612            <!-- <p>A</p>ˇ -->
 8613            <!-- <p>B</p>ˇ -->
 8614            <!-- <p>C</p>ˇ -->
 8615        "#
 8616        .unindent(),
 8617    );
 8618    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8619    cx.assert_editor_state(
 8620        &r#"
 8621            <p>A</p>ˇ
 8622            <p>B</p>ˇ
 8623            <p>C</p>ˇ
 8624        "#
 8625        .unindent(),
 8626    );
 8627
 8628    // Toggle comments for mixture of empty and non-empty selections, where
 8629    // multiple selections occupy a given line.
 8630    cx.set_state(
 8631        &r#"
 8632            <p>A«</p>
 8633            <p>ˇ»B</p>ˇ
 8634            <p>C«</p>
 8635            <p>ˇ»D</p>ˇ
 8636        "#
 8637        .unindent(),
 8638    );
 8639
 8640    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8641    cx.assert_editor_state(
 8642        &r#"
 8643            <!-- <p>A«</p>
 8644            <p>ˇ»B</p>ˇ -->
 8645            <!-- <p>C«</p>
 8646            <p>ˇ»D</p>ˇ -->
 8647        "#
 8648        .unindent(),
 8649    );
 8650    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8651    cx.assert_editor_state(
 8652        &r#"
 8653            <p>A«</p>
 8654            <p>ˇ»B</p>ˇ
 8655            <p>C«</p>
 8656            <p>ˇ»D</p>ˇ
 8657        "#
 8658        .unindent(),
 8659    );
 8660
 8661    // Toggle comments when different languages are active for different
 8662    // selections.
 8663    cx.set_state(
 8664        &r#"
 8665            ˇ<script>
 8666                ˇvar x = new Y();
 8667            ˇ</script>
 8668        "#
 8669        .unindent(),
 8670    );
 8671    cx.executor().run_until_parked();
 8672    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8673    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8674    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8675    cx.assert_editor_state(
 8676        &r#"
 8677            <!-- ˇ<script> -->
 8678                // ˇvar x = new Y();
 8679            // ˇ</script>
 8680        "#
 8681        .unindent(),
 8682    );
 8683}
 8684
 8685#[gpui::test]
 8686fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8687    init_test(cx, |_| {});
 8688
 8689    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8690    let multibuffer = cx.new_model(|cx| {
 8691        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8692        multibuffer.push_excerpts(
 8693            buffer.clone(),
 8694            [
 8695                ExcerptRange {
 8696                    context: Point::new(0, 0)..Point::new(0, 4),
 8697                    primary: None,
 8698                },
 8699                ExcerptRange {
 8700                    context: Point::new(1, 0)..Point::new(1, 4),
 8701                    primary: None,
 8702                },
 8703            ],
 8704            cx,
 8705        );
 8706        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8707        multibuffer
 8708    });
 8709
 8710    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8711    view.update(cx, |view, cx| {
 8712        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8713        view.change_selections(None, cx, |s| {
 8714            s.select_ranges([
 8715                Point::new(0, 0)..Point::new(0, 0),
 8716                Point::new(1, 0)..Point::new(1, 0),
 8717            ])
 8718        });
 8719
 8720        view.handle_input("X", cx);
 8721        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8722        assert_eq!(
 8723            view.selections.ranges(cx),
 8724            [
 8725                Point::new(0, 1)..Point::new(0, 1),
 8726                Point::new(1, 1)..Point::new(1, 1),
 8727            ]
 8728        );
 8729
 8730        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8731        view.change_selections(None, cx, |s| {
 8732            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8733        });
 8734        view.backspace(&Default::default(), cx);
 8735        assert_eq!(view.text(cx), "Xa\nbbb");
 8736        assert_eq!(
 8737            view.selections.ranges(cx),
 8738            [Point::new(1, 0)..Point::new(1, 0)]
 8739        );
 8740
 8741        view.change_selections(None, cx, |s| {
 8742            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8743        });
 8744        view.backspace(&Default::default(), cx);
 8745        assert_eq!(view.text(cx), "X\nbb");
 8746        assert_eq!(
 8747            view.selections.ranges(cx),
 8748            [Point::new(0, 1)..Point::new(0, 1)]
 8749        );
 8750    });
 8751}
 8752
 8753#[gpui::test]
 8754fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8755    init_test(cx, |_| {});
 8756
 8757    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8758    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8759        indoc! {"
 8760            [aaaa
 8761            (bbbb]
 8762            cccc)",
 8763        },
 8764        markers.clone(),
 8765    );
 8766    let excerpt_ranges = markers.into_iter().map(|marker| {
 8767        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8768        ExcerptRange {
 8769            context,
 8770            primary: None,
 8771        }
 8772    });
 8773    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8774    let multibuffer = cx.new_model(|cx| {
 8775        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8776        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8777        multibuffer
 8778    });
 8779
 8780    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8781    view.update(cx, |view, cx| {
 8782        let (expected_text, selection_ranges) = marked_text_ranges(
 8783            indoc! {"
 8784                aaaa
 8785                bˇbbb
 8786                bˇbbˇb
 8787                cccc"
 8788            },
 8789            true,
 8790        );
 8791        assert_eq!(view.text(cx), expected_text);
 8792        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8793
 8794        view.handle_input("X", cx);
 8795
 8796        let (expected_text, expected_selections) = marked_text_ranges(
 8797            indoc! {"
 8798                aaaa
 8799                bXˇbbXb
 8800                bXˇbbXˇb
 8801                cccc"
 8802            },
 8803            false,
 8804        );
 8805        assert_eq!(view.text(cx), expected_text);
 8806        assert_eq!(view.selections.ranges(cx), expected_selections);
 8807
 8808        view.newline(&Newline, cx);
 8809        let (expected_text, expected_selections) = marked_text_ranges(
 8810            indoc! {"
 8811                aaaa
 8812                bX
 8813                ˇbbX
 8814                b
 8815                bX
 8816                ˇbbX
 8817                ˇb
 8818                cccc"
 8819            },
 8820            false,
 8821        );
 8822        assert_eq!(view.text(cx), expected_text);
 8823        assert_eq!(view.selections.ranges(cx), expected_selections);
 8824    });
 8825}
 8826
 8827#[gpui::test]
 8828fn test_refresh_selections(cx: &mut TestAppContext) {
 8829    init_test(cx, |_| {});
 8830
 8831    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8832    let mut excerpt1_id = None;
 8833    let multibuffer = cx.new_model(|cx| {
 8834        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8835        excerpt1_id = multibuffer
 8836            .push_excerpts(
 8837                buffer.clone(),
 8838                [
 8839                    ExcerptRange {
 8840                        context: Point::new(0, 0)..Point::new(1, 4),
 8841                        primary: None,
 8842                    },
 8843                    ExcerptRange {
 8844                        context: Point::new(1, 0)..Point::new(2, 4),
 8845                        primary: None,
 8846                    },
 8847                ],
 8848                cx,
 8849            )
 8850            .into_iter()
 8851            .next();
 8852        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8853        multibuffer
 8854    });
 8855
 8856    let editor = cx.add_window(|cx| {
 8857        let mut editor = build_editor(multibuffer.clone(), cx);
 8858        let snapshot = editor.snapshot(cx);
 8859        editor.change_selections(None, cx, |s| {
 8860            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8861        });
 8862        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8863        assert_eq!(
 8864            editor.selections.ranges(cx),
 8865            [
 8866                Point::new(1, 3)..Point::new(1, 3),
 8867                Point::new(2, 1)..Point::new(2, 1),
 8868            ]
 8869        );
 8870        editor
 8871    });
 8872
 8873    // Refreshing selections is a no-op when excerpts haven't changed.
 8874    _ = editor.update(cx, |editor, cx| {
 8875        editor.change_selections(None, cx, |s| s.refresh());
 8876        assert_eq!(
 8877            editor.selections.ranges(cx),
 8878            [
 8879                Point::new(1, 3)..Point::new(1, 3),
 8880                Point::new(2, 1)..Point::new(2, 1),
 8881            ]
 8882        );
 8883    });
 8884
 8885    multibuffer.update(cx, |multibuffer, cx| {
 8886        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8887    });
 8888    _ = editor.update(cx, |editor, cx| {
 8889        // Removing an excerpt causes the first selection to become degenerate.
 8890        assert_eq!(
 8891            editor.selections.ranges(cx),
 8892            [
 8893                Point::new(0, 0)..Point::new(0, 0),
 8894                Point::new(0, 1)..Point::new(0, 1)
 8895            ]
 8896        );
 8897
 8898        // Refreshing selections will relocate the first selection to the original buffer
 8899        // location.
 8900        editor.change_selections(None, cx, |s| s.refresh());
 8901        assert_eq!(
 8902            editor.selections.ranges(cx),
 8903            [
 8904                Point::new(0, 1)..Point::new(0, 1),
 8905                Point::new(0, 3)..Point::new(0, 3)
 8906            ]
 8907        );
 8908        assert!(editor.selections.pending_anchor().is_some());
 8909    });
 8910}
 8911
 8912#[gpui::test]
 8913fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8914    init_test(cx, |_| {});
 8915
 8916    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8917    let mut excerpt1_id = None;
 8918    let multibuffer = cx.new_model(|cx| {
 8919        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8920        excerpt1_id = multibuffer
 8921            .push_excerpts(
 8922                buffer.clone(),
 8923                [
 8924                    ExcerptRange {
 8925                        context: Point::new(0, 0)..Point::new(1, 4),
 8926                        primary: None,
 8927                    },
 8928                    ExcerptRange {
 8929                        context: Point::new(1, 0)..Point::new(2, 4),
 8930                        primary: None,
 8931                    },
 8932                ],
 8933                cx,
 8934            )
 8935            .into_iter()
 8936            .next();
 8937        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8938        multibuffer
 8939    });
 8940
 8941    let editor = cx.add_window(|cx| {
 8942        let mut editor = build_editor(multibuffer.clone(), cx);
 8943        let snapshot = editor.snapshot(cx);
 8944        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8945        assert_eq!(
 8946            editor.selections.ranges(cx),
 8947            [Point::new(1, 3)..Point::new(1, 3)]
 8948        );
 8949        editor
 8950    });
 8951
 8952    multibuffer.update(cx, |multibuffer, cx| {
 8953        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8954    });
 8955    _ = editor.update(cx, |editor, cx| {
 8956        assert_eq!(
 8957            editor.selections.ranges(cx),
 8958            [Point::new(0, 0)..Point::new(0, 0)]
 8959        );
 8960
 8961        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8962        editor.change_selections(None, cx, |s| s.refresh());
 8963        assert_eq!(
 8964            editor.selections.ranges(cx),
 8965            [Point::new(0, 3)..Point::new(0, 3)]
 8966        );
 8967        assert!(editor.selections.pending_anchor().is_some());
 8968    });
 8969}
 8970
 8971#[gpui::test]
 8972async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8973    init_test(cx, |_| {});
 8974
 8975    let language = Arc::new(
 8976        Language::new(
 8977            LanguageConfig {
 8978                brackets: BracketPairConfig {
 8979                    pairs: vec![
 8980                        BracketPair {
 8981                            start: "{".to_string(),
 8982                            end: "}".to_string(),
 8983                            close: true,
 8984                            surround: true,
 8985                            newline: true,
 8986                        },
 8987                        BracketPair {
 8988                            start: "/* ".to_string(),
 8989                            end: " */".to_string(),
 8990                            close: true,
 8991                            surround: true,
 8992                            newline: true,
 8993                        },
 8994                    ],
 8995                    ..Default::default()
 8996                },
 8997                ..Default::default()
 8998            },
 8999            Some(tree_sitter_rust::LANGUAGE.into()),
 9000        )
 9001        .with_indents_query("")
 9002        .unwrap(),
 9003    );
 9004
 9005    let text = concat!(
 9006        "{   }\n",     //
 9007        "  x\n",       //
 9008        "  /*   */\n", //
 9009        "x\n",         //
 9010        "{{} }\n",     //
 9011    );
 9012
 9013    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9014    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9015    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9016    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9017        .await;
 9018
 9019    view.update(cx, |view, cx| {
 9020        view.change_selections(None, cx, |s| {
 9021            s.select_display_ranges([
 9022                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9023                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9024                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9025            ])
 9026        });
 9027        view.newline(&Newline, cx);
 9028
 9029        assert_eq!(
 9030            view.buffer().read(cx).read(cx).text(),
 9031            concat!(
 9032                "{ \n",    // Suppress rustfmt
 9033                "\n",      //
 9034                "}\n",     //
 9035                "  x\n",   //
 9036                "  /* \n", //
 9037                "  \n",    //
 9038                "  */\n",  //
 9039                "x\n",     //
 9040                "{{} \n",  //
 9041                "}\n",     //
 9042            )
 9043        );
 9044    });
 9045}
 9046
 9047#[gpui::test]
 9048fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9049    init_test(cx, |_| {});
 9050
 9051    let editor = cx.add_window(|cx| {
 9052        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9053        build_editor(buffer.clone(), cx)
 9054    });
 9055
 9056    _ = editor.update(cx, |editor, cx| {
 9057        struct Type1;
 9058        struct Type2;
 9059
 9060        let buffer = editor.buffer.read(cx).snapshot(cx);
 9061
 9062        let anchor_range =
 9063            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9064
 9065        editor.highlight_background::<Type1>(
 9066            &[
 9067                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9068                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9069                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9070                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9071            ],
 9072            |_| Hsla::red(),
 9073            cx,
 9074        );
 9075        editor.highlight_background::<Type2>(
 9076            &[
 9077                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9078                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9079                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9080                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9081            ],
 9082            |_| Hsla::green(),
 9083            cx,
 9084        );
 9085
 9086        let snapshot = editor.snapshot(cx);
 9087        let mut highlighted_ranges = editor.background_highlights_in_range(
 9088            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9089            &snapshot,
 9090            cx.theme().colors(),
 9091        );
 9092        // Enforce a consistent ordering based on color without relying on the ordering of the
 9093        // highlight's `TypeId` which is non-executor.
 9094        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9095        assert_eq!(
 9096            highlighted_ranges,
 9097            &[
 9098                (
 9099                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9100                    Hsla::red(),
 9101                ),
 9102                (
 9103                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9104                    Hsla::red(),
 9105                ),
 9106                (
 9107                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9108                    Hsla::green(),
 9109                ),
 9110                (
 9111                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9112                    Hsla::green(),
 9113                ),
 9114            ]
 9115        );
 9116        assert_eq!(
 9117            editor.background_highlights_in_range(
 9118                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9119                &snapshot,
 9120                cx.theme().colors(),
 9121            ),
 9122            &[(
 9123                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9124                Hsla::red(),
 9125            )]
 9126        );
 9127    });
 9128}
 9129
 9130#[gpui::test]
 9131async fn test_following(cx: &mut gpui::TestAppContext) {
 9132    init_test(cx, |_| {});
 9133
 9134    let fs = FakeFs::new(cx.executor());
 9135    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9136
 9137    let buffer = project.update(cx, |project, cx| {
 9138        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9139        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9140    });
 9141    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9142    let follower = cx.update(|cx| {
 9143        cx.open_window(
 9144            WindowOptions {
 9145                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9146                    gpui::Point::new(px(0.), px(0.)),
 9147                    gpui::Point::new(px(10.), px(80.)),
 9148                ))),
 9149                ..Default::default()
 9150            },
 9151            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9152        )
 9153        .unwrap()
 9154    });
 9155
 9156    let is_still_following = Rc::new(RefCell::new(true));
 9157    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9158    let pending_update = Rc::new(RefCell::new(None));
 9159    _ = follower.update(cx, {
 9160        let update = pending_update.clone();
 9161        let is_still_following = is_still_following.clone();
 9162        let follower_edit_event_count = follower_edit_event_count.clone();
 9163        |_, cx| {
 9164            cx.subscribe(
 9165                &leader.root_view(cx).unwrap(),
 9166                move |_, leader, event, cx| {
 9167                    leader
 9168                        .read(cx)
 9169                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9170                },
 9171            )
 9172            .detach();
 9173
 9174            cx.subscribe(
 9175                &follower.root_view(cx).unwrap(),
 9176                move |_, _, event: &EditorEvent, _cx| {
 9177                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9178                        *is_still_following.borrow_mut() = false;
 9179                    }
 9180
 9181                    if let EditorEvent::BufferEdited = event {
 9182                        *follower_edit_event_count.borrow_mut() += 1;
 9183                    }
 9184                },
 9185            )
 9186            .detach();
 9187        }
 9188    });
 9189
 9190    // Update the selections only
 9191    _ = leader.update(cx, |leader, cx| {
 9192        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9193    });
 9194    follower
 9195        .update(cx, |follower, cx| {
 9196            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9197        })
 9198        .unwrap()
 9199        .await
 9200        .unwrap();
 9201    _ = follower.update(cx, |follower, cx| {
 9202        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9203    });
 9204    assert!(*is_still_following.borrow());
 9205    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9206
 9207    // Update the scroll position only
 9208    _ = leader.update(cx, |leader, cx| {
 9209        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9210    });
 9211    follower
 9212        .update(cx, |follower, cx| {
 9213            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9214        })
 9215        .unwrap()
 9216        .await
 9217        .unwrap();
 9218    assert_eq!(
 9219        follower
 9220            .update(cx, |follower, cx| follower.scroll_position(cx))
 9221            .unwrap(),
 9222        gpui::Point::new(1.5, 3.5)
 9223    );
 9224    assert!(*is_still_following.borrow());
 9225    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9226
 9227    // Update the selections and scroll position. The follower's scroll position is updated
 9228    // via autoscroll, not via the leader's exact scroll position.
 9229    _ = leader.update(cx, |leader, cx| {
 9230        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9231        leader.request_autoscroll(Autoscroll::newest(), cx);
 9232        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9233    });
 9234    follower
 9235        .update(cx, |follower, cx| {
 9236            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9237        })
 9238        .unwrap()
 9239        .await
 9240        .unwrap();
 9241    _ = follower.update(cx, |follower, cx| {
 9242        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9243        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9244    });
 9245    assert!(*is_still_following.borrow());
 9246
 9247    // Creating a pending selection that precedes another selection
 9248    _ = leader.update(cx, |leader, cx| {
 9249        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9250        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9251    });
 9252    follower
 9253        .update(cx, |follower, cx| {
 9254            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9255        })
 9256        .unwrap()
 9257        .await
 9258        .unwrap();
 9259    _ = follower.update(cx, |follower, cx| {
 9260        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9261    });
 9262    assert!(*is_still_following.borrow());
 9263
 9264    // Extend the pending selection so that it surrounds another selection
 9265    _ = leader.update(cx, |leader, cx| {
 9266        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9267    });
 9268    follower
 9269        .update(cx, |follower, cx| {
 9270            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9271        })
 9272        .unwrap()
 9273        .await
 9274        .unwrap();
 9275    _ = follower.update(cx, |follower, cx| {
 9276        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9277    });
 9278
 9279    // Scrolling locally breaks the follow
 9280    _ = follower.update(cx, |follower, cx| {
 9281        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9282        follower.set_scroll_anchor(
 9283            ScrollAnchor {
 9284                anchor: top_anchor,
 9285                offset: gpui::Point::new(0.0, 0.5),
 9286            },
 9287            cx,
 9288        );
 9289    });
 9290    assert!(!(*is_still_following.borrow()));
 9291}
 9292
 9293#[gpui::test]
 9294async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9295    init_test(cx, |_| {});
 9296
 9297    let fs = FakeFs::new(cx.executor());
 9298    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9299    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9300    let pane = workspace
 9301        .update(cx, |workspace, _| workspace.active_pane().clone())
 9302        .unwrap();
 9303
 9304    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9305
 9306    let leader = pane.update(cx, |_, cx| {
 9307        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 9308        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9309    });
 9310
 9311    // Start following the editor when it has no excerpts.
 9312    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9313    let follower_1 = cx
 9314        .update_window(*workspace.deref(), |_, cx| {
 9315            Editor::from_state_proto(
 9316                workspace.root_view(cx).unwrap(),
 9317                ViewId {
 9318                    creator: Default::default(),
 9319                    id: 0,
 9320                },
 9321                &mut state_message,
 9322                cx,
 9323            )
 9324        })
 9325        .unwrap()
 9326        .unwrap()
 9327        .await
 9328        .unwrap();
 9329
 9330    let update_message = Rc::new(RefCell::new(None));
 9331    follower_1.update(cx, {
 9332        let update = update_message.clone();
 9333        |_, cx| {
 9334            cx.subscribe(&leader, move |_, leader, event, cx| {
 9335                leader
 9336                    .read(cx)
 9337                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9338            })
 9339            .detach();
 9340        }
 9341    });
 9342
 9343    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9344        (
 9345            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9346            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9347        )
 9348    });
 9349
 9350    // Insert some excerpts.
 9351    leader.update(cx, |leader, cx| {
 9352        leader.buffer.update(cx, |multibuffer, cx| {
 9353            let excerpt_ids = multibuffer.push_excerpts(
 9354                buffer_1.clone(),
 9355                [
 9356                    ExcerptRange {
 9357                        context: 1..6,
 9358                        primary: None,
 9359                    },
 9360                    ExcerptRange {
 9361                        context: 12..15,
 9362                        primary: None,
 9363                    },
 9364                    ExcerptRange {
 9365                        context: 0..3,
 9366                        primary: None,
 9367                    },
 9368                ],
 9369                cx,
 9370            );
 9371            multibuffer.insert_excerpts_after(
 9372                excerpt_ids[0],
 9373                buffer_2.clone(),
 9374                [
 9375                    ExcerptRange {
 9376                        context: 8..12,
 9377                        primary: None,
 9378                    },
 9379                    ExcerptRange {
 9380                        context: 0..6,
 9381                        primary: None,
 9382                    },
 9383                ],
 9384                cx,
 9385            );
 9386        });
 9387    });
 9388
 9389    // Apply the update of adding the excerpts.
 9390    follower_1
 9391        .update(cx, |follower, cx| {
 9392            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9393        })
 9394        .await
 9395        .unwrap();
 9396    assert_eq!(
 9397        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9398        leader.update(cx, |editor, cx| editor.text(cx))
 9399    );
 9400    update_message.borrow_mut().take();
 9401
 9402    // Start following separately after it already has excerpts.
 9403    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9404    let follower_2 = cx
 9405        .update_window(*workspace.deref(), |_, cx| {
 9406            Editor::from_state_proto(
 9407                workspace.root_view(cx).unwrap().clone(),
 9408                ViewId {
 9409                    creator: Default::default(),
 9410                    id: 0,
 9411                },
 9412                &mut state_message,
 9413                cx,
 9414            )
 9415        })
 9416        .unwrap()
 9417        .unwrap()
 9418        .await
 9419        .unwrap();
 9420    assert_eq!(
 9421        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9422        leader.update(cx, |editor, cx| editor.text(cx))
 9423    );
 9424
 9425    // Remove some excerpts.
 9426    leader.update(cx, |leader, cx| {
 9427        leader.buffer.update(cx, |multibuffer, cx| {
 9428            let excerpt_ids = multibuffer.excerpt_ids();
 9429            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9430            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9431        });
 9432    });
 9433
 9434    // Apply the update of removing the excerpts.
 9435    follower_1
 9436        .update(cx, |follower, cx| {
 9437            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9438        })
 9439        .await
 9440        .unwrap();
 9441    follower_2
 9442        .update(cx, |follower, cx| {
 9443            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9444        })
 9445        .await
 9446        .unwrap();
 9447    update_message.borrow_mut().take();
 9448    assert_eq!(
 9449        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9450        leader.update(cx, |editor, cx| editor.text(cx))
 9451    );
 9452}
 9453
 9454#[gpui::test]
 9455async fn go_to_prev_overlapping_diagnostic(
 9456    executor: BackgroundExecutor,
 9457    cx: &mut gpui::TestAppContext,
 9458) {
 9459    init_test(cx, |_| {});
 9460
 9461    let mut cx = EditorTestContext::new(cx).await;
 9462    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9463
 9464    cx.set_state(indoc! {"
 9465        ˇfn func(abc def: i32) -> u32 {
 9466        }
 9467    "});
 9468
 9469    cx.update(|cx| {
 9470        project.update(cx, |project, cx| {
 9471            project
 9472                .update_diagnostics(
 9473                    LanguageServerId(0),
 9474                    lsp::PublishDiagnosticsParams {
 9475                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9476                        version: None,
 9477                        diagnostics: vec![
 9478                            lsp::Diagnostic {
 9479                                range: lsp::Range::new(
 9480                                    lsp::Position::new(0, 11),
 9481                                    lsp::Position::new(0, 12),
 9482                                ),
 9483                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9484                                ..Default::default()
 9485                            },
 9486                            lsp::Diagnostic {
 9487                                range: lsp::Range::new(
 9488                                    lsp::Position::new(0, 12),
 9489                                    lsp::Position::new(0, 15),
 9490                                ),
 9491                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9492                                ..Default::default()
 9493                            },
 9494                            lsp::Diagnostic {
 9495                                range: lsp::Range::new(
 9496                                    lsp::Position::new(0, 25),
 9497                                    lsp::Position::new(0, 28),
 9498                                ),
 9499                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9500                                ..Default::default()
 9501                            },
 9502                        ],
 9503                    },
 9504                    &[],
 9505                    cx,
 9506                )
 9507                .unwrap()
 9508        });
 9509    });
 9510
 9511    executor.run_until_parked();
 9512
 9513    cx.update_editor(|editor, cx| {
 9514        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9515    });
 9516
 9517    cx.assert_editor_state(indoc! {"
 9518        fn func(abc def: i32) -> ˇu32 {
 9519        }
 9520    "});
 9521
 9522    cx.update_editor(|editor, cx| {
 9523        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9524    });
 9525
 9526    cx.assert_editor_state(indoc! {"
 9527        fn func(abc ˇdef: i32) -> u32 {
 9528        }
 9529    "});
 9530
 9531    cx.update_editor(|editor, cx| {
 9532        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9533    });
 9534
 9535    cx.assert_editor_state(indoc! {"
 9536        fn func(abcˇ def: i32) -> u32 {
 9537        }
 9538    "});
 9539
 9540    cx.update_editor(|editor, cx| {
 9541        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9542    });
 9543
 9544    cx.assert_editor_state(indoc! {"
 9545        fn func(abc def: i32) -> ˇu32 {
 9546        }
 9547    "});
 9548}
 9549
 9550#[gpui::test]
 9551async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9552    init_test(cx, |_| {});
 9553
 9554    let mut cx = EditorTestContext::new(cx).await;
 9555
 9556    cx.set_state(indoc! {"
 9557        fn func(abˇc def: i32) -> u32 {
 9558        }
 9559    "});
 9560    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9561
 9562    cx.update(|cx| {
 9563        project.update(cx, |project, cx| {
 9564            project.update_diagnostics(
 9565                LanguageServerId(0),
 9566                lsp::PublishDiagnosticsParams {
 9567                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9568                    version: None,
 9569                    diagnostics: vec![lsp::Diagnostic {
 9570                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9571                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9572                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9573                        ..Default::default()
 9574                    }],
 9575                },
 9576                &[],
 9577                cx,
 9578            )
 9579        })
 9580    }).unwrap();
 9581    cx.run_until_parked();
 9582    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9583    cx.run_until_parked();
 9584    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9585}
 9586
 9587#[gpui::test]
 9588async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9589    init_test(cx, |_| {});
 9590
 9591    let mut cx = EditorTestContext::new(cx).await;
 9592
 9593    let diff_base = r#"
 9594        use some::mod;
 9595
 9596        const A: u32 = 42;
 9597
 9598        fn main() {
 9599            println!("hello");
 9600
 9601            println!("world");
 9602        }
 9603        "#
 9604    .unindent();
 9605
 9606    // Edits are modified, removed, modified, added
 9607    cx.set_state(
 9608        &r#"
 9609        use some::modified;
 9610
 9611        ˇ
 9612        fn main() {
 9613            println!("hello there");
 9614
 9615            println!("around the");
 9616            println!("world");
 9617        }
 9618        "#
 9619        .unindent(),
 9620    );
 9621
 9622    cx.set_diff_base(Some(&diff_base));
 9623    executor.run_until_parked();
 9624
 9625    cx.update_editor(|editor, cx| {
 9626        //Wrap around the bottom of the buffer
 9627        for _ in 0..3 {
 9628            editor.go_to_hunk(&GoToHunk, cx);
 9629        }
 9630    });
 9631
 9632    cx.assert_editor_state(
 9633        &r#"
 9634        ˇuse some::modified;
 9635
 9636
 9637        fn main() {
 9638            println!("hello there");
 9639
 9640            println!("around the");
 9641            println!("world");
 9642        }
 9643        "#
 9644        .unindent(),
 9645    );
 9646
 9647    cx.update_editor(|editor, cx| {
 9648        //Wrap around the top of the buffer
 9649        for _ in 0..2 {
 9650            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9651        }
 9652    });
 9653
 9654    cx.assert_editor_state(
 9655        &r#"
 9656        use some::modified;
 9657
 9658
 9659        fn main() {
 9660        ˇ    println!("hello there");
 9661
 9662            println!("around the");
 9663            println!("world");
 9664        }
 9665        "#
 9666        .unindent(),
 9667    );
 9668
 9669    cx.update_editor(|editor, cx| {
 9670        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9671    });
 9672
 9673    cx.assert_editor_state(
 9674        &r#"
 9675        use some::modified;
 9676
 9677        ˇ
 9678        fn main() {
 9679            println!("hello there");
 9680
 9681            println!("around the");
 9682            println!("world");
 9683        }
 9684        "#
 9685        .unindent(),
 9686    );
 9687
 9688    cx.update_editor(|editor, cx| {
 9689        for _ in 0..3 {
 9690            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9691        }
 9692    });
 9693
 9694    cx.assert_editor_state(
 9695        &r#"
 9696        use some::modified;
 9697
 9698
 9699        fn main() {
 9700        ˇ    println!("hello there");
 9701
 9702            println!("around the");
 9703            println!("world");
 9704        }
 9705        "#
 9706        .unindent(),
 9707    );
 9708
 9709    cx.update_editor(|editor, cx| {
 9710        editor.fold(&Fold, cx);
 9711
 9712        //Make sure that the fold only gets one hunk
 9713        for _ in 0..4 {
 9714            editor.go_to_hunk(&GoToHunk, cx);
 9715        }
 9716    });
 9717
 9718    cx.assert_editor_state(
 9719        &r#"
 9720        ˇuse some::modified;
 9721
 9722
 9723        fn main() {
 9724            println!("hello there");
 9725
 9726            println!("around the");
 9727            println!("world");
 9728        }
 9729        "#
 9730        .unindent(),
 9731    );
 9732}
 9733
 9734#[test]
 9735fn test_split_words() {
 9736    fn split(text: &str) -> Vec<&str> {
 9737        split_words(text).collect()
 9738    }
 9739
 9740    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9741    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9742    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9743    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9744    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9745    assert_eq!(split("helloworld"), &["helloworld"]);
 9746
 9747    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9748}
 9749
 9750#[gpui::test]
 9751async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9752    init_test(cx, |_| {});
 9753
 9754    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9755    let mut assert = |before, after| {
 9756        let _state_context = cx.set_state(before);
 9757        cx.update_editor(|editor, cx| {
 9758            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9759        });
 9760        cx.assert_editor_state(after);
 9761    };
 9762
 9763    // Outside bracket jumps to outside of matching bracket
 9764    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9765    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9766
 9767    // Inside bracket jumps to inside of matching bracket
 9768    assert("console.log(ˇvar);", "console.log(varˇ);");
 9769    assert("console.log(varˇ);", "console.log(ˇvar);");
 9770
 9771    // When outside a bracket and inside, favor jumping to the inside bracket
 9772    assert(
 9773        "console.log('foo', [1, 2, 3]ˇ);",
 9774        "console.log(ˇ'foo', [1, 2, 3]);",
 9775    );
 9776    assert(
 9777        "console.log(ˇ'foo', [1, 2, 3]);",
 9778        "console.log('foo', [1, 2, 3]ˇ);",
 9779    );
 9780
 9781    // Bias forward if two options are equally likely
 9782    assert(
 9783        "let result = curried_fun()ˇ();",
 9784        "let result = curried_fun()()ˇ;",
 9785    );
 9786
 9787    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9788    assert(
 9789        indoc! {"
 9790            function test() {
 9791                console.log('test')ˇ
 9792            }"},
 9793        indoc! {"
 9794            function test() {
 9795                console.logˇ('test')
 9796            }"},
 9797    );
 9798}
 9799
 9800#[gpui::test]
 9801async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9802    init_test(cx, |_| {});
 9803
 9804    let fs = FakeFs::new(cx.executor());
 9805    fs.insert_tree(
 9806        "/a",
 9807        json!({
 9808            "main.rs": "fn main() { let a = 5; }",
 9809            "other.rs": "// Test file",
 9810        }),
 9811    )
 9812    .await;
 9813    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9814
 9815    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9816    language_registry.add(Arc::new(Language::new(
 9817        LanguageConfig {
 9818            name: "Rust".into(),
 9819            matcher: LanguageMatcher {
 9820                path_suffixes: vec!["rs".to_string()],
 9821                ..Default::default()
 9822            },
 9823            brackets: BracketPairConfig {
 9824                pairs: vec![BracketPair {
 9825                    start: "{".to_string(),
 9826                    end: "}".to_string(),
 9827                    close: true,
 9828                    surround: true,
 9829                    newline: true,
 9830                }],
 9831                disabled_scopes_by_bracket_ix: Vec::new(),
 9832            },
 9833            ..Default::default()
 9834        },
 9835        Some(tree_sitter_rust::LANGUAGE.into()),
 9836    )));
 9837    let mut fake_servers = language_registry.register_fake_lsp(
 9838        "Rust",
 9839        FakeLspAdapter {
 9840            capabilities: lsp::ServerCapabilities {
 9841                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9842                    first_trigger_character: "{".to_string(),
 9843                    more_trigger_character: None,
 9844                }),
 9845                ..Default::default()
 9846            },
 9847            ..Default::default()
 9848        },
 9849    );
 9850
 9851    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9852
 9853    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9854
 9855    let worktree_id = workspace
 9856        .update(cx, |workspace, cx| {
 9857            workspace.project().update(cx, |project, cx| {
 9858                project.worktrees(cx).next().unwrap().read(cx).id()
 9859            })
 9860        })
 9861        .unwrap();
 9862
 9863    let buffer = project
 9864        .update(cx, |project, cx| {
 9865            project.open_local_buffer("/a/main.rs", cx)
 9866        })
 9867        .await
 9868        .unwrap();
 9869    cx.executor().run_until_parked();
 9870    cx.executor().start_waiting();
 9871    let fake_server = fake_servers.next().await.unwrap();
 9872    let editor_handle = workspace
 9873        .update(cx, |workspace, cx| {
 9874            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9875        })
 9876        .unwrap()
 9877        .await
 9878        .unwrap()
 9879        .downcast::<Editor>()
 9880        .unwrap();
 9881
 9882    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9883        assert_eq!(
 9884            params.text_document_position.text_document.uri,
 9885            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9886        );
 9887        assert_eq!(
 9888            params.text_document_position.position,
 9889            lsp::Position::new(0, 21),
 9890        );
 9891
 9892        Ok(Some(vec![lsp::TextEdit {
 9893            new_text: "]".to_string(),
 9894            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9895        }]))
 9896    });
 9897
 9898    editor_handle.update(cx, |editor, cx| {
 9899        editor.focus(cx);
 9900        editor.change_selections(None, cx, |s| {
 9901            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9902        });
 9903        editor.handle_input("{", cx);
 9904    });
 9905
 9906    cx.executor().run_until_parked();
 9907
 9908    buffer.update(cx, |buffer, _| {
 9909        assert_eq!(
 9910            buffer.text(),
 9911            "fn main() { let a = {5}; }",
 9912            "No extra braces from on type formatting should appear in the buffer"
 9913        )
 9914    });
 9915}
 9916
 9917#[gpui::test]
 9918async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9919    init_test(cx, |_| {});
 9920
 9921    let fs = FakeFs::new(cx.executor());
 9922    fs.insert_tree(
 9923        "/a",
 9924        json!({
 9925            "main.rs": "fn main() { let a = 5; }",
 9926            "other.rs": "// Test file",
 9927        }),
 9928    )
 9929    .await;
 9930
 9931    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9932
 9933    let server_restarts = Arc::new(AtomicUsize::new(0));
 9934    let closure_restarts = Arc::clone(&server_restarts);
 9935    let language_server_name = "test language server";
 9936    let language_name: LanguageName = "Rust".into();
 9937
 9938    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9939    language_registry.add(Arc::new(Language::new(
 9940        LanguageConfig {
 9941            name: language_name.clone(),
 9942            matcher: LanguageMatcher {
 9943                path_suffixes: vec!["rs".to_string()],
 9944                ..Default::default()
 9945            },
 9946            ..Default::default()
 9947        },
 9948        Some(tree_sitter_rust::LANGUAGE.into()),
 9949    )));
 9950    let mut fake_servers = language_registry.register_fake_lsp(
 9951        "Rust",
 9952        FakeLspAdapter {
 9953            name: language_server_name,
 9954            initialization_options: Some(json!({
 9955                "testOptionValue": true
 9956            })),
 9957            initializer: Some(Box::new(move |fake_server| {
 9958                let task_restarts = Arc::clone(&closure_restarts);
 9959                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9960                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9961                    futures::future::ready(Ok(()))
 9962                });
 9963            })),
 9964            ..Default::default()
 9965        },
 9966    );
 9967
 9968    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9969    let _buffer = project
 9970        .update(cx, |project, cx| {
 9971            project.open_local_buffer("/a/main.rs", cx)
 9972        })
 9973        .await
 9974        .unwrap();
 9975    let _fake_server = fake_servers.next().await.unwrap();
 9976    update_test_language_settings(cx, |language_settings| {
 9977        language_settings.languages.insert(
 9978            language_name.clone(),
 9979            LanguageSettingsContent {
 9980                tab_size: NonZeroU32::new(8),
 9981                ..Default::default()
 9982            },
 9983        );
 9984    });
 9985    cx.executor().run_until_parked();
 9986    assert_eq!(
 9987        server_restarts.load(atomic::Ordering::Acquire),
 9988        0,
 9989        "Should not restart LSP server on an unrelated change"
 9990    );
 9991
 9992    update_test_project_settings(cx, |project_settings| {
 9993        project_settings.lsp.insert(
 9994            "Some other server name".into(),
 9995            LspSettings {
 9996                binary: None,
 9997                settings: None,
 9998                initialization_options: Some(json!({
 9999                    "some other init value": false
10000                })),
10001            },
10002        );
10003    });
10004    cx.executor().run_until_parked();
10005    assert_eq!(
10006        server_restarts.load(atomic::Ordering::Acquire),
10007        0,
10008        "Should not restart LSP server on an unrelated LSP settings change"
10009    );
10010
10011    update_test_project_settings(cx, |project_settings| {
10012        project_settings.lsp.insert(
10013            language_server_name.into(),
10014            LspSettings {
10015                binary: None,
10016                settings: None,
10017                initialization_options: Some(json!({
10018                    "anotherInitValue": false
10019                })),
10020            },
10021        );
10022    });
10023    cx.executor().run_until_parked();
10024    assert_eq!(
10025        server_restarts.load(atomic::Ordering::Acquire),
10026        1,
10027        "Should restart LSP server on a related LSP settings change"
10028    );
10029
10030    update_test_project_settings(cx, |project_settings| {
10031        project_settings.lsp.insert(
10032            language_server_name.into(),
10033            LspSettings {
10034                binary: None,
10035                settings: None,
10036                initialization_options: Some(json!({
10037                    "anotherInitValue": false
10038                })),
10039            },
10040        );
10041    });
10042    cx.executor().run_until_parked();
10043    assert_eq!(
10044        server_restarts.load(atomic::Ordering::Acquire),
10045        1,
10046        "Should not restart LSP server on a related LSP settings change that is the same"
10047    );
10048
10049    update_test_project_settings(cx, |project_settings| {
10050        project_settings.lsp.insert(
10051            language_server_name.into(),
10052            LspSettings {
10053                binary: None,
10054                settings: None,
10055                initialization_options: None,
10056            },
10057        );
10058    });
10059    cx.executor().run_until_parked();
10060    assert_eq!(
10061        server_restarts.load(atomic::Ordering::Acquire),
10062        2,
10063        "Should restart LSP server on another related LSP settings change"
10064    );
10065}
10066
10067#[gpui::test]
10068async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10069    init_test(cx, |_| {});
10070
10071    let mut cx = EditorLspTestContext::new_rust(
10072        lsp::ServerCapabilities {
10073            completion_provider: Some(lsp::CompletionOptions {
10074                trigger_characters: Some(vec![".".to_string()]),
10075                resolve_provider: Some(true),
10076                ..Default::default()
10077            }),
10078            ..Default::default()
10079        },
10080        cx,
10081    )
10082    .await;
10083
10084    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10085    cx.simulate_keystroke(".");
10086    let completion_item = lsp::CompletionItem {
10087        label: "some".into(),
10088        kind: Some(lsp::CompletionItemKind::SNIPPET),
10089        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10090        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10091            kind: lsp::MarkupKind::Markdown,
10092            value: "```rust\nSome(2)\n```".to_string(),
10093        })),
10094        deprecated: Some(false),
10095        sort_text: Some("fffffff2".to_string()),
10096        filter_text: Some("some".to_string()),
10097        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10098        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10099            range: lsp::Range {
10100                start: lsp::Position {
10101                    line: 0,
10102                    character: 22,
10103                },
10104                end: lsp::Position {
10105                    line: 0,
10106                    character: 22,
10107                },
10108            },
10109            new_text: "Some(2)".to_string(),
10110        })),
10111        additional_text_edits: Some(vec![lsp::TextEdit {
10112            range: lsp::Range {
10113                start: lsp::Position {
10114                    line: 0,
10115                    character: 20,
10116                },
10117                end: lsp::Position {
10118                    line: 0,
10119                    character: 22,
10120                },
10121            },
10122            new_text: "".to_string(),
10123        }]),
10124        ..Default::default()
10125    };
10126
10127    let closure_completion_item = completion_item.clone();
10128    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10129        let task_completion_item = closure_completion_item.clone();
10130        async move {
10131            Ok(Some(lsp::CompletionResponse::Array(vec![
10132                task_completion_item,
10133            ])))
10134        }
10135    });
10136
10137    request.next().await;
10138
10139    cx.condition(|editor, _| editor.context_menu_visible())
10140        .await;
10141    let apply_additional_edits = cx.update_editor(|editor, cx| {
10142        editor
10143            .confirm_completion(&ConfirmCompletion::default(), cx)
10144            .unwrap()
10145    });
10146    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10147
10148    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10149        let task_completion_item = completion_item.clone();
10150        async move { Ok(task_completion_item) }
10151    })
10152    .next()
10153    .await
10154    .unwrap();
10155    apply_additional_edits.await.unwrap();
10156    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10157}
10158
10159#[gpui::test]
10160async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10161    init_test(cx, |_| {});
10162
10163    let mut cx = EditorLspTestContext::new(
10164        Language::new(
10165            LanguageConfig {
10166                matcher: LanguageMatcher {
10167                    path_suffixes: vec!["jsx".into()],
10168                    ..Default::default()
10169                },
10170                overrides: [(
10171                    "element".into(),
10172                    LanguageConfigOverride {
10173                        word_characters: Override::Set(['-'].into_iter().collect()),
10174                        ..Default::default()
10175                    },
10176                )]
10177                .into_iter()
10178                .collect(),
10179                ..Default::default()
10180            },
10181            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10182        )
10183        .with_override_query("(jsx_self_closing_element) @element")
10184        .unwrap(),
10185        lsp::ServerCapabilities {
10186            completion_provider: Some(lsp::CompletionOptions {
10187                trigger_characters: Some(vec![":".to_string()]),
10188                ..Default::default()
10189            }),
10190            ..Default::default()
10191        },
10192        cx,
10193    )
10194    .await;
10195
10196    cx.lsp
10197        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10198            Ok(Some(lsp::CompletionResponse::Array(vec![
10199                lsp::CompletionItem {
10200                    label: "bg-blue".into(),
10201                    ..Default::default()
10202                },
10203                lsp::CompletionItem {
10204                    label: "bg-red".into(),
10205                    ..Default::default()
10206                },
10207                lsp::CompletionItem {
10208                    label: "bg-yellow".into(),
10209                    ..Default::default()
10210                },
10211            ])))
10212        });
10213
10214    cx.set_state(r#"<p class="bgˇ" />"#);
10215
10216    // Trigger completion when typing a dash, because the dash is an extra
10217    // word character in the 'element' scope, which contains the cursor.
10218    cx.simulate_keystroke("-");
10219    cx.executor().run_until_parked();
10220    cx.update_editor(|editor, _| {
10221        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10222            assert_eq!(
10223                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10224                &["bg-red", "bg-blue", "bg-yellow"]
10225            );
10226        } else {
10227            panic!("expected completion menu to be open");
10228        }
10229    });
10230
10231    cx.simulate_keystroke("l");
10232    cx.executor().run_until_parked();
10233    cx.update_editor(|editor, _| {
10234        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10235            assert_eq!(
10236                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10237                &["bg-blue", "bg-yellow"]
10238            );
10239        } else {
10240            panic!("expected completion menu to be open");
10241        }
10242    });
10243
10244    // When filtering completions, consider the character after the '-' to
10245    // be the start of a subword.
10246    cx.set_state(r#"<p class="yelˇ" />"#);
10247    cx.simulate_keystroke("l");
10248    cx.executor().run_until_parked();
10249    cx.update_editor(|editor, _| {
10250        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10251            assert_eq!(
10252                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10253                &["bg-yellow"]
10254            );
10255        } else {
10256            panic!("expected completion menu to be open");
10257        }
10258    });
10259}
10260
10261#[gpui::test]
10262async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10263    init_test(cx, |settings| {
10264        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10265            FormatterList(vec![Formatter::Prettier].into()),
10266        ))
10267    });
10268
10269    let fs = FakeFs::new(cx.executor());
10270    fs.insert_file("/file.ts", Default::default()).await;
10271
10272    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10273    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10274
10275    language_registry.add(Arc::new(Language::new(
10276        LanguageConfig {
10277            name: "TypeScript".into(),
10278            matcher: LanguageMatcher {
10279                path_suffixes: vec!["ts".to_string()],
10280                ..Default::default()
10281            },
10282            ..Default::default()
10283        },
10284        Some(tree_sitter_rust::LANGUAGE.into()),
10285    )));
10286    update_test_language_settings(cx, |settings| {
10287        settings.defaults.prettier = Some(PrettierSettings {
10288            allowed: true,
10289            ..PrettierSettings::default()
10290        });
10291    });
10292
10293    let test_plugin = "test_plugin";
10294    let _ = language_registry.register_fake_lsp(
10295        "TypeScript",
10296        FakeLspAdapter {
10297            prettier_plugins: vec![test_plugin],
10298            ..Default::default()
10299        },
10300    );
10301
10302    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10303    let buffer = project
10304        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10305        .await
10306        .unwrap();
10307
10308    let buffer_text = "one\ntwo\nthree\n";
10309    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10310    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10311    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10312
10313    editor
10314        .update(cx, |editor, cx| {
10315            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10316        })
10317        .unwrap()
10318        .await;
10319    assert_eq!(
10320        editor.update(cx, |editor, cx| editor.text(cx)),
10321        buffer_text.to_string() + prettier_format_suffix,
10322        "Test prettier formatting was not applied to the original buffer text",
10323    );
10324
10325    update_test_language_settings(cx, |settings| {
10326        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10327    });
10328    let format = editor.update(cx, |editor, cx| {
10329        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10330    });
10331    format.await.unwrap();
10332    assert_eq!(
10333        editor.update(cx, |editor, cx| editor.text(cx)),
10334        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10335        "Autoformatting (via test prettier) was not applied to the original buffer text",
10336    );
10337}
10338
10339#[gpui::test]
10340async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10341    init_test(cx, |_| {});
10342    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10343    let base_text = indoc! {r#"struct Row;
10344struct Row1;
10345struct Row2;
10346
10347struct Row4;
10348struct Row5;
10349struct Row6;
10350
10351struct Row8;
10352struct Row9;
10353struct Row10;"#};
10354
10355    // When addition hunks are not adjacent to carets, no hunk revert is performed
10356    assert_hunk_revert(
10357        indoc! {r#"struct Row;
10358                   struct Row1;
10359                   struct Row1.1;
10360                   struct Row1.2;
10361                   struct Row2;ˇ
10362
10363                   struct Row4;
10364                   struct Row5;
10365                   struct Row6;
10366
10367                   struct Row8;
10368                   ˇstruct Row9;
10369                   struct Row9.1;
10370                   struct Row9.2;
10371                   struct Row9.3;
10372                   struct Row10;"#},
10373        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10374        indoc! {r#"struct Row;
10375                   struct Row1;
10376                   struct Row1.1;
10377                   struct Row1.2;
10378                   struct Row2;ˇ
10379
10380                   struct Row4;
10381                   struct Row5;
10382                   struct Row6;
10383
10384                   struct Row8;
10385                   ˇstruct Row9;
10386                   struct Row9.1;
10387                   struct Row9.2;
10388                   struct Row9.3;
10389                   struct Row10;"#},
10390        base_text,
10391        &mut cx,
10392    );
10393    // Same for selections
10394    assert_hunk_revert(
10395        indoc! {r#"struct Row;
10396                   struct Row1;
10397                   struct Row2;
10398                   struct Row2.1;
10399                   struct Row2.2;
10400                   «ˇ
10401                   struct Row4;
10402                   struct» Row5;
10403                   «struct Row6;
10404                   ˇ»
10405                   struct Row9.1;
10406                   struct Row9.2;
10407                   struct Row9.3;
10408                   struct Row8;
10409                   struct Row9;
10410                   struct Row10;"#},
10411        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10412        indoc! {r#"struct Row;
10413                   struct Row1;
10414                   struct Row2;
10415                   struct Row2.1;
10416                   struct Row2.2;
10417                   «ˇ
10418                   struct Row4;
10419                   struct» Row5;
10420                   «struct Row6;
10421                   ˇ»
10422                   struct Row9.1;
10423                   struct Row9.2;
10424                   struct Row9.3;
10425                   struct Row8;
10426                   struct Row9;
10427                   struct Row10;"#},
10428        base_text,
10429        &mut cx,
10430    );
10431
10432    // When carets and selections intersect the addition hunks, those are reverted.
10433    // Adjacent carets got merged.
10434    assert_hunk_revert(
10435        indoc! {r#"struct Row;
10436                   ˇ// something on the top
10437                   struct Row1;
10438                   struct Row2;
10439                   struct Roˇw3.1;
10440                   struct Row2.2;
10441                   struct Row2.3;ˇ
10442
10443                   struct Row4;
10444                   struct ˇRow5.1;
10445                   struct Row5.2;
10446                   struct «Rowˇ»5.3;
10447                   struct Row5;
10448                   struct Row6;
10449                   ˇ
10450                   struct Row9.1;
10451                   struct «Rowˇ»9.2;
10452                   struct «ˇRow»9.3;
10453                   struct Row8;
10454                   struct Row9;
10455                   «ˇ// something on bottom»
10456                   struct Row10;"#},
10457        vec![
10458            DiffHunkStatus::Added,
10459            DiffHunkStatus::Added,
10460            DiffHunkStatus::Added,
10461            DiffHunkStatus::Added,
10462            DiffHunkStatus::Added,
10463        ],
10464        indoc! {r#"struct Row;
10465                   ˇstruct Row1;
10466                   struct Row2;
10467                   ˇ
10468                   struct Row4;
10469                   ˇstruct Row5;
10470                   struct Row6;
10471                   ˇ
10472                   ˇstruct Row8;
10473                   struct Row9;
10474                   ˇstruct Row10;"#},
10475        base_text,
10476        &mut cx,
10477    );
10478}
10479
10480#[gpui::test]
10481async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10482    init_test(cx, |_| {});
10483    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10484    let base_text = indoc! {r#"struct Row;
10485struct Row1;
10486struct Row2;
10487
10488struct Row4;
10489struct Row5;
10490struct Row6;
10491
10492struct Row8;
10493struct Row9;
10494struct Row10;"#};
10495
10496    // Modification hunks behave the same as the addition ones.
10497    assert_hunk_revert(
10498        indoc! {r#"struct Row;
10499                   struct Row1;
10500                   struct Row33;
10501                   ˇ
10502                   struct Row4;
10503                   struct Row5;
10504                   struct Row6;
10505                   ˇ
10506                   struct Row99;
10507                   struct Row9;
10508                   struct Row10;"#},
10509        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10510        indoc! {r#"struct Row;
10511                   struct Row1;
10512                   struct Row33;
10513                   ˇ
10514                   struct Row4;
10515                   struct Row5;
10516                   struct Row6;
10517                   ˇ
10518                   struct Row99;
10519                   struct Row9;
10520                   struct Row10;"#},
10521        base_text,
10522        &mut cx,
10523    );
10524    assert_hunk_revert(
10525        indoc! {r#"struct Row;
10526                   struct Row1;
10527                   struct Row33;
10528                   «ˇ
10529                   struct Row4;
10530                   struct» Row5;
10531                   «struct Row6;
10532                   ˇ»
10533                   struct Row99;
10534                   struct Row9;
10535                   struct Row10;"#},
10536        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10537        indoc! {r#"struct Row;
10538                   struct Row1;
10539                   struct Row33;
10540                   «ˇ
10541                   struct Row4;
10542                   struct» Row5;
10543                   «struct Row6;
10544                   ˇ»
10545                   struct Row99;
10546                   struct Row9;
10547                   struct Row10;"#},
10548        base_text,
10549        &mut cx,
10550    );
10551
10552    assert_hunk_revert(
10553        indoc! {r#"ˇstruct Row1.1;
10554                   struct Row1;
10555                   «ˇstr»uct Row22;
10556
10557                   struct ˇRow44;
10558                   struct Row5;
10559                   struct «Rˇ»ow66;ˇ
10560
10561                   «struˇ»ct Row88;
10562                   struct Row9;
10563                   struct Row1011;ˇ"#},
10564        vec![
10565            DiffHunkStatus::Modified,
10566            DiffHunkStatus::Modified,
10567            DiffHunkStatus::Modified,
10568            DiffHunkStatus::Modified,
10569            DiffHunkStatus::Modified,
10570            DiffHunkStatus::Modified,
10571        ],
10572        indoc! {r#"struct Row;
10573                   ˇstruct Row1;
10574                   struct Row2;
10575                   ˇ
10576                   struct Row4;
10577                   ˇstruct Row5;
10578                   struct Row6;
10579                   ˇ
10580                   struct Row8;
10581                   ˇstruct Row9;
10582                   struct Row10;ˇ"#},
10583        base_text,
10584        &mut cx,
10585    );
10586}
10587
10588#[gpui::test]
10589async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10590    init_test(cx, |_| {});
10591    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10592    let base_text = indoc! {r#"struct Row;
10593struct Row1;
10594struct Row2;
10595
10596struct Row4;
10597struct Row5;
10598struct Row6;
10599
10600struct Row8;
10601struct Row9;
10602struct Row10;"#};
10603
10604    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10605    assert_hunk_revert(
10606        indoc! {r#"struct Row;
10607                   struct Row2;
10608
10609                   ˇstruct Row4;
10610                   struct Row5;
10611                   struct Row6;
10612                   ˇ
10613                   struct Row8;
10614                   struct Row10;"#},
10615        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10616        indoc! {r#"struct Row;
10617                   struct Row2;
10618
10619                   ˇstruct Row4;
10620                   struct Row5;
10621                   struct Row6;
10622                   ˇ
10623                   struct Row8;
10624                   struct Row10;"#},
10625        base_text,
10626        &mut cx,
10627    );
10628    assert_hunk_revert(
10629        indoc! {r#"struct Row;
10630                   struct Row2;
10631
10632                   «ˇstruct Row4;
10633                   struct» Row5;
10634                   «struct Row6;
10635                   ˇ»
10636                   struct Row8;
10637                   struct Row10;"#},
10638        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10639        indoc! {r#"struct Row;
10640                   struct Row2;
10641
10642                   «ˇstruct Row4;
10643                   struct» Row5;
10644                   «struct Row6;
10645                   ˇ»
10646                   struct Row8;
10647                   struct Row10;"#},
10648        base_text,
10649        &mut cx,
10650    );
10651
10652    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10653    assert_hunk_revert(
10654        indoc! {r#"struct Row;
10655                   ˇstruct Row2;
10656
10657                   struct Row4;
10658                   struct Row5;
10659                   struct Row6;
10660
10661                   struct Row8;ˇ
10662                   struct Row10;"#},
10663        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10664        indoc! {r#"struct Row;
10665                   struct Row1;
10666                   ˇstruct Row2;
10667
10668                   struct Row4;
10669                   struct Row5;
10670                   struct Row6;
10671
10672                   struct Row8;ˇ
10673                   struct Row9;
10674                   struct Row10;"#},
10675        base_text,
10676        &mut cx,
10677    );
10678    assert_hunk_revert(
10679        indoc! {r#"struct Row;
10680                   struct Row2«ˇ;
10681                   struct Row4;
10682                   struct» Row5;
10683                   «struct Row6;
10684
10685                   struct Row8;ˇ»
10686                   struct Row10;"#},
10687        vec![
10688            DiffHunkStatus::Removed,
10689            DiffHunkStatus::Removed,
10690            DiffHunkStatus::Removed,
10691        ],
10692        indoc! {r#"struct Row;
10693                   struct Row1;
10694                   struct Row2«ˇ;
10695
10696                   struct Row4;
10697                   struct» Row5;
10698                   «struct Row6;
10699
10700                   struct Row8;ˇ»
10701                   struct Row9;
10702                   struct Row10;"#},
10703        base_text,
10704        &mut cx,
10705    );
10706}
10707
10708#[gpui::test]
10709async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10710    init_test(cx, |_| {});
10711
10712    let cols = 4;
10713    let rows = 10;
10714    let sample_text_1 = sample_text(rows, cols, 'a');
10715    assert_eq!(
10716        sample_text_1,
10717        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10718    );
10719    let sample_text_2 = sample_text(rows, cols, 'l');
10720    assert_eq!(
10721        sample_text_2,
10722        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10723    );
10724    let sample_text_3 = sample_text(rows, cols, 'v');
10725    assert_eq!(
10726        sample_text_3,
10727        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10728    );
10729
10730    fn diff_every_buffer_row(
10731        buffer: &Model<Buffer>,
10732        sample_text: String,
10733        cols: usize,
10734        cx: &mut gpui::TestAppContext,
10735    ) {
10736        // revert first character in each row, creating one large diff hunk per buffer
10737        let is_first_char = |offset: usize| offset % cols == 0;
10738        buffer.update(cx, |buffer, cx| {
10739            buffer.set_text(
10740                sample_text
10741                    .chars()
10742                    .enumerate()
10743                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10744                    .collect::<String>(),
10745                cx,
10746            );
10747            buffer.set_diff_base(Some(sample_text), cx);
10748        });
10749        cx.executor().run_until_parked();
10750    }
10751
10752    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10753    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10754
10755    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10756    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10757
10758    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10759    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10760
10761    let multibuffer = cx.new_model(|cx| {
10762        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10763        multibuffer.push_excerpts(
10764            buffer_1.clone(),
10765            [
10766                ExcerptRange {
10767                    context: Point::new(0, 0)..Point::new(3, 0),
10768                    primary: None,
10769                },
10770                ExcerptRange {
10771                    context: Point::new(5, 0)..Point::new(7, 0),
10772                    primary: None,
10773                },
10774                ExcerptRange {
10775                    context: Point::new(9, 0)..Point::new(10, 4),
10776                    primary: None,
10777                },
10778            ],
10779            cx,
10780        );
10781        multibuffer.push_excerpts(
10782            buffer_2.clone(),
10783            [
10784                ExcerptRange {
10785                    context: Point::new(0, 0)..Point::new(3, 0),
10786                    primary: None,
10787                },
10788                ExcerptRange {
10789                    context: Point::new(5, 0)..Point::new(7, 0),
10790                    primary: None,
10791                },
10792                ExcerptRange {
10793                    context: Point::new(9, 0)..Point::new(10, 4),
10794                    primary: None,
10795                },
10796            ],
10797            cx,
10798        );
10799        multibuffer.push_excerpts(
10800            buffer_3.clone(),
10801            [
10802                ExcerptRange {
10803                    context: Point::new(0, 0)..Point::new(3, 0),
10804                    primary: None,
10805                },
10806                ExcerptRange {
10807                    context: Point::new(5, 0)..Point::new(7, 0),
10808                    primary: None,
10809                },
10810                ExcerptRange {
10811                    context: Point::new(9, 0)..Point::new(10, 4),
10812                    primary: None,
10813                },
10814            ],
10815            cx,
10816        );
10817        multibuffer
10818    });
10819
10820    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10821    editor.update(cx, |editor, cx| {
10822        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");
10823        editor.select_all(&SelectAll, cx);
10824        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10825    });
10826    cx.executor().run_until_parked();
10827    // When all ranges are selected, all buffer hunks are reverted.
10828    editor.update(cx, |editor, cx| {
10829        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");
10830    });
10831    buffer_1.update(cx, |buffer, _| {
10832        assert_eq!(buffer.text(), sample_text_1);
10833    });
10834    buffer_2.update(cx, |buffer, _| {
10835        assert_eq!(buffer.text(), sample_text_2);
10836    });
10837    buffer_3.update(cx, |buffer, _| {
10838        assert_eq!(buffer.text(), sample_text_3);
10839    });
10840
10841    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10842    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10843    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10844    editor.update(cx, |editor, cx| {
10845        editor.change_selections(None, cx, |s| {
10846            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10847        });
10848        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10849    });
10850    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10851    // but not affect buffer_2 and its related excerpts.
10852    editor.update(cx, |editor, cx| {
10853        assert_eq!(
10854            editor.text(cx),
10855            "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"
10856        );
10857    });
10858    buffer_1.update(cx, |buffer, _| {
10859        assert_eq!(buffer.text(), sample_text_1);
10860    });
10861    buffer_2.update(cx, |buffer, _| {
10862        assert_eq!(
10863            buffer.text(),
10864            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10865        );
10866    });
10867    buffer_3.update(cx, |buffer, _| {
10868        assert_eq!(
10869            buffer.text(),
10870            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10871        );
10872    });
10873}
10874
10875#[gpui::test]
10876async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10877    init_test(cx, |_| {});
10878
10879    let cols = 4;
10880    let rows = 10;
10881    let sample_text_1 = sample_text(rows, cols, 'a');
10882    assert_eq!(
10883        sample_text_1,
10884        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10885    );
10886    let sample_text_2 = sample_text(rows, cols, 'l');
10887    assert_eq!(
10888        sample_text_2,
10889        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10890    );
10891    let sample_text_3 = sample_text(rows, cols, 'v');
10892    assert_eq!(
10893        sample_text_3,
10894        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10895    );
10896
10897    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10898    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10899    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10900
10901    let multi_buffer = cx.new_model(|cx| {
10902        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10903        multibuffer.push_excerpts(
10904            buffer_1.clone(),
10905            [
10906                ExcerptRange {
10907                    context: Point::new(0, 0)..Point::new(3, 0),
10908                    primary: None,
10909                },
10910                ExcerptRange {
10911                    context: Point::new(5, 0)..Point::new(7, 0),
10912                    primary: None,
10913                },
10914                ExcerptRange {
10915                    context: Point::new(9, 0)..Point::new(10, 4),
10916                    primary: None,
10917                },
10918            ],
10919            cx,
10920        );
10921        multibuffer.push_excerpts(
10922            buffer_2.clone(),
10923            [
10924                ExcerptRange {
10925                    context: Point::new(0, 0)..Point::new(3, 0),
10926                    primary: None,
10927                },
10928                ExcerptRange {
10929                    context: Point::new(5, 0)..Point::new(7, 0),
10930                    primary: None,
10931                },
10932                ExcerptRange {
10933                    context: Point::new(9, 0)..Point::new(10, 4),
10934                    primary: None,
10935                },
10936            ],
10937            cx,
10938        );
10939        multibuffer.push_excerpts(
10940            buffer_3.clone(),
10941            [
10942                ExcerptRange {
10943                    context: Point::new(0, 0)..Point::new(3, 0),
10944                    primary: None,
10945                },
10946                ExcerptRange {
10947                    context: Point::new(5, 0)..Point::new(7, 0),
10948                    primary: None,
10949                },
10950                ExcerptRange {
10951                    context: Point::new(9, 0)..Point::new(10, 4),
10952                    primary: None,
10953                },
10954            ],
10955            cx,
10956        );
10957        multibuffer
10958    });
10959
10960    let fs = FakeFs::new(cx.executor());
10961    fs.insert_tree(
10962        "/a",
10963        json!({
10964            "main.rs": sample_text_1,
10965            "other.rs": sample_text_2,
10966            "lib.rs": sample_text_3,
10967        }),
10968    )
10969    .await;
10970    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10971    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10972    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10973    let multi_buffer_editor = cx.new_view(|cx| {
10974        Editor::new(
10975            EditorMode::Full,
10976            multi_buffer,
10977            Some(project.clone()),
10978            true,
10979            cx,
10980        )
10981    });
10982    let multibuffer_item_id = workspace
10983        .update(cx, |workspace, cx| {
10984            assert!(
10985                workspace.active_item(cx).is_none(),
10986                "active item should be None before the first item is added"
10987            );
10988            workspace.add_item_to_active_pane(
10989                Box::new(multi_buffer_editor.clone()),
10990                None,
10991                true,
10992                cx,
10993            );
10994            let active_item = workspace
10995                .active_item(cx)
10996                .expect("should have an active item after adding the multi buffer");
10997            assert!(
10998                !active_item.is_singleton(cx),
10999                "A multi buffer was expected to active after adding"
11000            );
11001            active_item.item_id()
11002        })
11003        .unwrap();
11004    cx.executor().run_until_parked();
11005
11006    multi_buffer_editor.update(cx, |editor, cx| {
11007        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11008        editor.open_excerpts(&OpenExcerpts, cx);
11009    });
11010    cx.executor().run_until_parked();
11011    let first_item_id = workspace
11012        .update(cx, |workspace, cx| {
11013            let active_item = workspace
11014                .active_item(cx)
11015                .expect("should have an active item after navigating into the 1st buffer");
11016            let first_item_id = active_item.item_id();
11017            assert_ne!(
11018                first_item_id, multibuffer_item_id,
11019                "Should navigate into the 1st buffer and activate it"
11020            );
11021            assert!(
11022                active_item.is_singleton(cx),
11023                "New active item should be a singleton buffer"
11024            );
11025            assert_eq!(
11026                active_item
11027                    .act_as::<Editor>(cx)
11028                    .expect("should have navigated into an editor for the 1st buffer")
11029                    .read(cx)
11030                    .text(cx),
11031                sample_text_1
11032            );
11033
11034            workspace
11035                .go_back(workspace.active_pane().downgrade(), cx)
11036                .detach_and_log_err(cx);
11037
11038            first_item_id
11039        })
11040        .unwrap();
11041    cx.executor().run_until_parked();
11042    workspace
11043        .update(cx, |workspace, cx| {
11044            let active_item = workspace
11045                .active_item(cx)
11046                .expect("should have an active item after navigating back");
11047            assert_eq!(
11048                active_item.item_id(),
11049                multibuffer_item_id,
11050                "Should navigate back to the multi buffer"
11051            );
11052            assert!(!active_item.is_singleton(cx));
11053        })
11054        .unwrap();
11055
11056    multi_buffer_editor.update(cx, |editor, cx| {
11057        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11058            s.select_ranges(Some(39..40))
11059        });
11060        editor.open_excerpts(&OpenExcerpts, cx);
11061    });
11062    cx.executor().run_until_parked();
11063    let second_item_id = workspace
11064        .update(cx, |workspace, cx| {
11065            let active_item = workspace
11066                .active_item(cx)
11067                .expect("should have an active item after navigating into the 2nd buffer");
11068            let second_item_id = active_item.item_id();
11069            assert_ne!(
11070                second_item_id, multibuffer_item_id,
11071                "Should navigate away from the multibuffer"
11072            );
11073            assert_ne!(
11074                second_item_id, first_item_id,
11075                "Should navigate into the 2nd buffer and activate it"
11076            );
11077            assert!(
11078                active_item.is_singleton(cx),
11079                "New active item should be a singleton buffer"
11080            );
11081            assert_eq!(
11082                active_item
11083                    .act_as::<Editor>(cx)
11084                    .expect("should have navigated into an editor")
11085                    .read(cx)
11086                    .text(cx),
11087                sample_text_2
11088            );
11089
11090            workspace
11091                .go_back(workspace.active_pane().downgrade(), cx)
11092                .detach_and_log_err(cx);
11093
11094            second_item_id
11095        })
11096        .unwrap();
11097    cx.executor().run_until_parked();
11098    workspace
11099        .update(cx, |workspace, cx| {
11100            let active_item = workspace
11101                .active_item(cx)
11102                .expect("should have an active item after navigating back from the 2nd buffer");
11103            assert_eq!(
11104                active_item.item_id(),
11105                multibuffer_item_id,
11106                "Should navigate back from the 2nd buffer to the multi buffer"
11107            );
11108            assert!(!active_item.is_singleton(cx));
11109        })
11110        .unwrap();
11111
11112    multi_buffer_editor.update(cx, |editor, cx| {
11113        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11114            s.select_ranges(Some(60..70))
11115        });
11116        editor.open_excerpts(&OpenExcerpts, cx);
11117    });
11118    cx.executor().run_until_parked();
11119    workspace
11120        .update(cx, |workspace, cx| {
11121            let active_item = workspace
11122                .active_item(cx)
11123                .expect("should have an active item after navigating into the 3rd buffer");
11124            let third_item_id = active_item.item_id();
11125            assert_ne!(
11126                third_item_id, multibuffer_item_id,
11127                "Should navigate into the 3rd buffer and activate it"
11128            );
11129            assert_ne!(third_item_id, first_item_id);
11130            assert_ne!(third_item_id, second_item_id);
11131            assert!(
11132                active_item.is_singleton(cx),
11133                "New active item should be a singleton buffer"
11134            );
11135            assert_eq!(
11136                active_item
11137                    .act_as::<Editor>(cx)
11138                    .expect("should have navigated into an editor")
11139                    .read(cx)
11140                    .text(cx),
11141                sample_text_3
11142            );
11143
11144            workspace
11145                .go_back(workspace.active_pane().downgrade(), cx)
11146                .detach_and_log_err(cx);
11147        })
11148        .unwrap();
11149    cx.executor().run_until_parked();
11150    workspace
11151        .update(cx, |workspace, cx| {
11152            let active_item = workspace
11153                .active_item(cx)
11154                .expect("should have an active item after navigating back from the 3rd buffer");
11155            assert_eq!(
11156                active_item.item_id(),
11157                multibuffer_item_id,
11158                "Should navigate back from the 3rd buffer to the multi buffer"
11159            );
11160            assert!(!active_item.is_singleton(cx));
11161        })
11162        .unwrap();
11163}
11164
11165#[gpui::test]
11166async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11167    init_test(cx, |_| {});
11168
11169    let mut cx = EditorTestContext::new(cx).await;
11170
11171    let diff_base = r#"
11172        use some::mod;
11173
11174        const A: u32 = 42;
11175
11176        fn main() {
11177            println!("hello");
11178
11179            println!("world");
11180        }
11181        "#
11182    .unindent();
11183
11184    cx.set_state(
11185        &r#"
11186        use some::modified;
11187
11188        ˇ
11189        fn main() {
11190            println!("hello there");
11191
11192            println!("around the");
11193            println!("world");
11194        }
11195        "#
11196        .unindent(),
11197    );
11198
11199    cx.set_diff_base(Some(&diff_base));
11200    executor.run_until_parked();
11201    let unexpanded_hunks = vec![
11202        (
11203            "use some::mod;\n".to_string(),
11204            DiffHunkStatus::Modified,
11205            DisplayRow(0)..DisplayRow(1),
11206        ),
11207        (
11208            "const A: u32 = 42;\n".to_string(),
11209            DiffHunkStatus::Removed,
11210            DisplayRow(2)..DisplayRow(2),
11211        ),
11212        (
11213            "    println!(\"hello\");\n".to_string(),
11214            DiffHunkStatus::Modified,
11215            DisplayRow(4)..DisplayRow(5),
11216        ),
11217        (
11218            "".to_string(),
11219            DiffHunkStatus::Added,
11220            DisplayRow(6)..DisplayRow(7),
11221        ),
11222    ];
11223    cx.update_editor(|editor, cx| {
11224        let snapshot = editor.snapshot(cx);
11225        let all_hunks = editor_hunks(editor, &snapshot, cx);
11226        assert_eq!(all_hunks, unexpanded_hunks);
11227    });
11228
11229    cx.update_editor(|editor, cx| {
11230        for _ in 0..4 {
11231            editor.go_to_hunk(&GoToHunk, cx);
11232            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11233        }
11234    });
11235    executor.run_until_parked();
11236    cx.assert_editor_state(
11237        &r#"
11238        use some::modified;
11239
11240        ˇ
11241        fn main() {
11242            println!("hello there");
11243
11244            println!("around the");
11245            println!("world");
11246        }
11247        "#
11248        .unindent(),
11249    );
11250    cx.update_editor(|editor, cx| {
11251        let snapshot = editor.snapshot(cx);
11252        let all_hunks = editor_hunks(editor, &snapshot, cx);
11253        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11254        assert_eq!(
11255            expanded_hunks_background_highlights(editor, cx),
11256            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
11257            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11258        );
11259        assert_eq!(
11260            all_hunks,
11261            vec![
11262                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
11263                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
11264                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
11265                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
11266            ],
11267            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11268            (from modified and removed hunks)"
11269        );
11270        assert_eq!(
11271            all_hunks, all_expanded_hunks,
11272            "Editor hunks should not change and all be expanded"
11273        );
11274    });
11275
11276    cx.update_editor(|editor, cx| {
11277        editor.cancel(&Cancel, cx);
11278
11279        let snapshot = editor.snapshot(cx);
11280        let all_hunks = editor_hunks(editor, &snapshot, cx);
11281        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11282        assert_eq!(
11283            expanded_hunks_background_highlights(editor, cx),
11284            Vec::new(),
11285            "After cancelling in editor, no git highlights should be left"
11286        );
11287        assert_eq!(
11288            all_expanded_hunks,
11289            Vec::new(),
11290            "After cancelling in editor, no hunks should be expanded"
11291        );
11292        assert_eq!(
11293            all_hunks, unexpanded_hunks,
11294            "After cancelling in editor, regular hunks' coordinates should get back to normal"
11295        );
11296    });
11297}
11298
11299#[gpui::test]
11300async fn test_toggled_diff_base_change(
11301    executor: BackgroundExecutor,
11302    cx: &mut gpui::TestAppContext,
11303) {
11304    init_test(cx, |_| {});
11305
11306    let mut cx = EditorTestContext::new(cx).await;
11307
11308    let diff_base = r#"
11309        use some::mod1;
11310        use some::mod2;
11311
11312        const A: u32 = 42;
11313        const B: u32 = 42;
11314        const C: u32 = 42;
11315
11316        fn main(ˇ) {
11317            println!("hello");
11318
11319            println!("world");
11320        }
11321        "#
11322    .unindent();
11323
11324    cx.set_state(
11325        &r#"
11326        use some::mod2;
11327
11328        const A: u32 = 42;
11329        const C: u32 = 42;
11330
11331        fn main(ˇ) {
11332            //println!("hello");
11333
11334            println!("world");
11335            //
11336            //
11337        }
11338        "#
11339        .unindent(),
11340    );
11341
11342    cx.set_diff_base(Some(&diff_base));
11343    executor.run_until_parked();
11344    cx.update_editor(|editor, cx| {
11345        let snapshot = editor.snapshot(cx);
11346        let all_hunks = editor_hunks(editor, &snapshot, cx);
11347        assert_eq!(
11348            all_hunks,
11349            vec![
11350                (
11351                    "use some::mod1;\n".to_string(),
11352                    DiffHunkStatus::Removed,
11353                    DisplayRow(0)..DisplayRow(0)
11354                ),
11355                (
11356                    "const B: u32 = 42;\n".to_string(),
11357                    DiffHunkStatus::Removed,
11358                    DisplayRow(3)..DisplayRow(3)
11359                ),
11360                (
11361                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11362                    DiffHunkStatus::Modified,
11363                    DisplayRow(5)..DisplayRow(7)
11364                ),
11365                (
11366                    "".to_string(),
11367                    DiffHunkStatus::Added,
11368                    DisplayRow(9)..DisplayRow(11)
11369                ),
11370            ]
11371        );
11372    });
11373
11374    cx.update_editor(|editor, cx| {
11375        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11376    });
11377    executor.run_until_parked();
11378    cx.assert_editor_state(
11379        &r#"
11380        use some::mod2;
11381
11382        const A: u32 = 42;
11383        const C: u32 = 42;
11384
11385        fn main(ˇ) {
11386            //println!("hello");
11387
11388            println!("world");
11389            //
11390            //
11391        }
11392        "#
11393        .unindent(),
11394    );
11395    cx.update_editor(|editor, cx| {
11396        let snapshot = editor.snapshot(cx);
11397        let all_hunks = editor_hunks(editor, &snapshot, cx);
11398        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11399        assert_eq!(
11400            expanded_hunks_background_highlights(editor, cx),
11401            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
11402            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11403        );
11404        assert_eq!(
11405            all_hunks,
11406            vec![
11407                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
11408                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
11409                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
11410                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
11411            ],
11412            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11413            (from modified and removed hunks)"
11414        );
11415        assert_eq!(
11416            all_hunks, all_expanded_hunks,
11417            "Editor hunks should not change and all be expanded"
11418        );
11419    });
11420
11421    cx.set_diff_base(Some("new diff base!"));
11422    executor.run_until_parked();
11423
11424    cx.update_editor(|editor, cx| {
11425        let snapshot = editor.snapshot(cx);
11426        let all_hunks = editor_hunks(editor, &snapshot, cx);
11427        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11428        assert_eq!(
11429            expanded_hunks_background_highlights(editor, cx),
11430            Vec::new(),
11431            "After diff base is changed, old git highlights should be removed"
11432        );
11433        assert_eq!(
11434            all_expanded_hunks,
11435            Vec::new(),
11436            "After diff base is changed, old git hunk expansions should be removed"
11437        );
11438        assert_eq!(
11439            all_hunks,
11440            vec![(
11441                "new diff base!".to_string(),
11442                DiffHunkStatus::Modified,
11443                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
11444            )],
11445            "After diff base is changed, hunks should update"
11446        );
11447    });
11448}
11449
11450#[gpui::test]
11451async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11452    init_test(cx, |_| {});
11453
11454    let mut cx = EditorTestContext::new(cx).await;
11455
11456    let diff_base = r#"
11457        use some::mod1;
11458        use some::mod2;
11459
11460        const A: u32 = 42;
11461        const B: u32 = 42;
11462        const C: u32 = 42;
11463
11464        fn main(ˇ) {
11465            println!("hello");
11466
11467            println!("world");
11468        }
11469
11470        fn another() {
11471            println!("another");
11472        }
11473
11474        fn another2() {
11475            println!("another2");
11476        }
11477        "#
11478    .unindent();
11479
11480    cx.set_state(
11481        &r#"
11482        «use some::mod2;
11483
11484        const A: u32 = 42;
11485        const C: u32 = 42;
11486
11487        fn main() {
11488            //println!("hello");
11489
11490            println!("world");
11491            //
11492            //ˇ»
11493        }
11494
11495        fn another() {
11496            println!("another");
11497            println!("another");
11498        }
11499
11500            println!("another2");
11501        }
11502        "#
11503        .unindent(),
11504    );
11505
11506    cx.set_diff_base(Some(&diff_base));
11507    executor.run_until_parked();
11508    cx.update_editor(|editor, cx| {
11509        let snapshot = editor.snapshot(cx);
11510        let all_hunks = editor_hunks(editor, &snapshot, cx);
11511        assert_eq!(
11512            all_hunks,
11513            vec![
11514                (
11515                    "use some::mod1;\n".to_string(),
11516                    DiffHunkStatus::Removed,
11517                    DisplayRow(0)..DisplayRow(0)
11518                ),
11519                (
11520                    "const B: u32 = 42;\n".to_string(),
11521                    DiffHunkStatus::Removed,
11522                    DisplayRow(3)..DisplayRow(3)
11523                ),
11524                (
11525                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11526                    DiffHunkStatus::Modified,
11527                    DisplayRow(5)..DisplayRow(7)
11528                ),
11529                (
11530                    "".to_string(),
11531                    DiffHunkStatus::Added,
11532                    DisplayRow(9)..DisplayRow(11)
11533                ),
11534                (
11535                    "".to_string(),
11536                    DiffHunkStatus::Added,
11537                    DisplayRow(15)..DisplayRow(16)
11538                ),
11539                (
11540                    "fn another2() {\n".to_string(),
11541                    DiffHunkStatus::Removed,
11542                    DisplayRow(18)..DisplayRow(18)
11543                ),
11544            ]
11545        );
11546    });
11547
11548    cx.update_editor(|editor, cx| {
11549        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11550    });
11551    executor.run_until_parked();
11552    cx.assert_editor_state(
11553        &r#"
11554        «use some::mod2;
11555
11556        const A: u32 = 42;
11557        const C: u32 = 42;
11558
11559        fn main() {
11560            //println!("hello");
11561
11562            println!("world");
11563            //
11564            //ˇ»
11565        }
11566
11567        fn another() {
11568            println!("another");
11569            println!("another");
11570        }
11571
11572            println!("another2");
11573        }
11574        "#
11575        .unindent(),
11576    );
11577    cx.update_editor(|editor, cx| {
11578        let snapshot = editor.snapshot(cx);
11579        let all_hunks = editor_hunks(editor, &snapshot, cx);
11580        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11581        assert_eq!(
11582            expanded_hunks_background_highlights(editor, cx),
11583            vec![
11584                DisplayRow(9)..=DisplayRow(10),
11585                DisplayRow(13)..=DisplayRow(14),
11586                DisplayRow(19)..=DisplayRow(19)
11587            ]
11588        );
11589        assert_eq!(
11590            all_hunks,
11591            vec![
11592                (
11593                    "use some::mod1;\n".to_string(),
11594                    DiffHunkStatus::Removed,
11595                    DisplayRow(1)..DisplayRow(1)
11596                ),
11597                (
11598                    "const B: u32 = 42;\n".to_string(),
11599                    DiffHunkStatus::Removed,
11600                    DisplayRow(5)..DisplayRow(5)
11601                ),
11602                (
11603                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11604                    DiffHunkStatus::Modified,
11605                    DisplayRow(9)..DisplayRow(11)
11606                ),
11607                (
11608                    "".to_string(),
11609                    DiffHunkStatus::Added,
11610                    DisplayRow(13)..DisplayRow(15)
11611                ),
11612                (
11613                    "".to_string(),
11614                    DiffHunkStatus::Added,
11615                    DisplayRow(19)..DisplayRow(20)
11616                ),
11617                (
11618                    "fn another2() {\n".to_string(),
11619                    DiffHunkStatus::Removed,
11620                    DisplayRow(23)..DisplayRow(23)
11621                ),
11622            ],
11623        );
11624        assert_eq!(all_hunks, all_expanded_hunks);
11625    });
11626
11627    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11628    cx.executor().run_until_parked();
11629    cx.assert_editor_state(
11630        &r#"
11631        «use some::mod2;
11632
11633        const A: u32 = 42;
11634        const C: u32 = 42;
11635
11636        fn main() {
11637            //println!("hello");
11638
11639            println!("world");
11640            //
11641            //ˇ»
11642        }
11643
11644        fn another() {
11645            println!("another");
11646            println!("another");
11647        }
11648
11649            println!("another2");
11650        }
11651        "#
11652        .unindent(),
11653    );
11654    cx.update_editor(|editor, cx| {
11655        let snapshot = editor.snapshot(cx);
11656        let all_hunks = editor_hunks(editor, &snapshot, cx);
11657        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11658        assert_eq!(
11659            expanded_hunks_background_highlights(editor, cx),
11660            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11661            "Only one hunk is left not folded, its highlight should be visible"
11662        );
11663        assert_eq!(
11664            all_hunks,
11665            vec![
11666                (
11667                    "use some::mod1;\n".to_string(),
11668                    DiffHunkStatus::Removed,
11669                    DisplayRow(0)..DisplayRow(0)
11670                ),
11671                (
11672                    "const B: u32 = 42;\n".to_string(),
11673                    DiffHunkStatus::Removed,
11674                    DisplayRow(0)..DisplayRow(0)
11675                ),
11676                (
11677                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11678                    DiffHunkStatus::Modified,
11679                    DisplayRow(0)..DisplayRow(0)
11680                ),
11681                (
11682                    "".to_string(),
11683                    DiffHunkStatus::Added,
11684                    DisplayRow(0)..DisplayRow(1)
11685                ),
11686                (
11687                    "".to_string(),
11688                    DiffHunkStatus::Added,
11689                    DisplayRow(5)..DisplayRow(6)
11690                ),
11691                (
11692                    "fn another2() {\n".to_string(),
11693                    DiffHunkStatus::Removed,
11694                    DisplayRow(9)..DisplayRow(9)
11695                ),
11696            ],
11697            "Hunk list should still return shifted folded hunks"
11698        );
11699        assert_eq!(
11700            all_expanded_hunks,
11701            vec![
11702                (
11703                    "".to_string(),
11704                    DiffHunkStatus::Added,
11705                    DisplayRow(5)..DisplayRow(6)
11706                ),
11707                (
11708                    "fn another2() {\n".to_string(),
11709                    DiffHunkStatus::Removed,
11710                    DisplayRow(9)..DisplayRow(9)
11711                ),
11712            ],
11713            "Only non-folded hunks should be left expanded"
11714        );
11715    });
11716
11717    cx.update_editor(|editor, cx| {
11718        editor.select_all(&SelectAll, cx);
11719        editor.unfold_lines(&UnfoldLines, cx);
11720    });
11721    cx.executor().run_until_parked();
11722    cx.assert_editor_state(
11723        &r#"
11724        «use some::mod2;
11725
11726        const A: u32 = 42;
11727        const C: u32 = 42;
11728
11729        fn main() {
11730            //println!("hello");
11731
11732            println!("world");
11733            //
11734            //
11735        }
11736
11737        fn another() {
11738            println!("another");
11739            println!("another");
11740        }
11741
11742            println!("another2");
11743        }
11744        ˇ»"#
11745        .unindent(),
11746    );
11747    cx.update_editor(|editor, cx| {
11748        let snapshot = editor.snapshot(cx);
11749        let all_hunks = editor_hunks(editor, &snapshot, cx);
11750        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11751        assert_eq!(
11752            expanded_hunks_background_highlights(editor, cx),
11753            vec![
11754                DisplayRow(9)..=DisplayRow(10),
11755                DisplayRow(13)..=DisplayRow(14),
11756                DisplayRow(19)..=DisplayRow(19)
11757            ],
11758            "After unfolding, all hunk diffs should be visible again"
11759        );
11760        assert_eq!(
11761            all_hunks,
11762            vec![
11763                (
11764                    "use some::mod1;\n".to_string(),
11765                    DiffHunkStatus::Removed,
11766                    DisplayRow(1)..DisplayRow(1)
11767                ),
11768                (
11769                    "const B: u32 = 42;\n".to_string(),
11770                    DiffHunkStatus::Removed,
11771                    DisplayRow(5)..DisplayRow(5)
11772                ),
11773                (
11774                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11775                    DiffHunkStatus::Modified,
11776                    DisplayRow(9)..DisplayRow(11)
11777                ),
11778                (
11779                    "".to_string(),
11780                    DiffHunkStatus::Added,
11781                    DisplayRow(13)..DisplayRow(15)
11782                ),
11783                (
11784                    "".to_string(),
11785                    DiffHunkStatus::Added,
11786                    DisplayRow(19)..DisplayRow(20)
11787                ),
11788                (
11789                    "fn another2() {\n".to_string(),
11790                    DiffHunkStatus::Removed,
11791                    DisplayRow(23)..DisplayRow(23)
11792                ),
11793            ],
11794        );
11795        assert_eq!(all_hunks, all_expanded_hunks);
11796    });
11797}
11798
11799#[gpui::test]
11800async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11801    init_test(cx, |_| {});
11802
11803    let cols = 4;
11804    let rows = 10;
11805    let sample_text_1 = sample_text(rows, cols, 'a');
11806    assert_eq!(
11807        sample_text_1,
11808        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11809    );
11810    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11811    let sample_text_2 = sample_text(rows, cols, 'l');
11812    assert_eq!(
11813        sample_text_2,
11814        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11815    );
11816    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11817    let sample_text_3 = sample_text(rows, cols, 'v');
11818    assert_eq!(
11819        sample_text_3,
11820        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11821    );
11822    let modified_sample_text_3 =
11823        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11824    let buffer_1 = cx.new_model(|cx| {
11825        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11826        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11827        buffer
11828    });
11829    let buffer_2 = cx.new_model(|cx| {
11830        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11831        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11832        buffer
11833    });
11834    let buffer_3 = cx.new_model(|cx| {
11835        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11836        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11837        buffer
11838    });
11839
11840    let multi_buffer = cx.new_model(|cx| {
11841        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11842        multibuffer.push_excerpts(
11843            buffer_1.clone(),
11844            [
11845                ExcerptRange {
11846                    context: Point::new(0, 0)..Point::new(3, 0),
11847                    primary: None,
11848                },
11849                ExcerptRange {
11850                    context: Point::new(5, 0)..Point::new(7, 0),
11851                    primary: None,
11852                },
11853                ExcerptRange {
11854                    context: Point::new(9, 0)..Point::new(10, 4),
11855                    primary: None,
11856                },
11857            ],
11858            cx,
11859        );
11860        multibuffer.push_excerpts(
11861            buffer_2.clone(),
11862            [
11863                ExcerptRange {
11864                    context: Point::new(0, 0)..Point::new(3, 0),
11865                    primary: None,
11866                },
11867                ExcerptRange {
11868                    context: Point::new(5, 0)..Point::new(7, 0),
11869                    primary: None,
11870                },
11871                ExcerptRange {
11872                    context: Point::new(9, 0)..Point::new(10, 4),
11873                    primary: None,
11874                },
11875            ],
11876            cx,
11877        );
11878        multibuffer.push_excerpts(
11879            buffer_3.clone(),
11880            [
11881                ExcerptRange {
11882                    context: Point::new(0, 0)..Point::new(3, 0),
11883                    primary: None,
11884                },
11885                ExcerptRange {
11886                    context: Point::new(5, 0)..Point::new(7, 0),
11887                    primary: None,
11888                },
11889                ExcerptRange {
11890                    context: Point::new(9, 0)..Point::new(10, 4),
11891                    primary: None,
11892                },
11893            ],
11894            cx,
11895        );
11896        multibuffer
11897    });
11898
11899    let fs = FakeFs::new(cx.executor());
11900    fs.insert_tree(
11901        "/a",
11902        json!({
11903            "main.rs": modified_sample_text_1,
11904            "other.rs": modified_sample_text_2,
11905            "lib.rs": modified_sample_text_3,
11906        }),
11907    )
11908    .await;
11909
11910    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11911    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11912    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11913    let multi_buffer_editor = cx.new_view(|cx| {
11914        Editor::new(
11915            EditorMode::Full,
11916            multi_buffer,
11917            Some(project.clone()),
11918            true,
11919            cx,
11920        )
11921    });
11922    cx.executor().run_until_parked();
11923
11924    let expected_all_hunks = vec![
11925        (
11926            "bbbb\n".to_string(),
11927            DiffHunkStatus::Removed,
11928            DisplayRow(4)..DisplayRow(4),
11929        ),
11930        (
11931            "nnnn\n".to_string(),
11932            DiffHunkStatus::Modified,
11933            DisplayRow(21)..DisplayRow(22),
11934        ),
11935        (
11936            "".to_string(),
11937            DiffHunkStatus::Added,
11938            DisplayRow(41)..DisplayRow(42),
11939        ),
11940    ];
11941    let expected_all_hunks_shifted = vec![
11942        (
11943            "bbbb\n".to_string(),
11944            DiffHunkStatus::Removed,
11945            DisplayRow(5)..DisplayRow(5),
11946        ),
11947        (
11948            "nnnn\n".to_string(),
11949            DiffHunkStatus::Modified,
11950            DisplayRow(23)..DisplayRow(24),
11951        ),
11952        (
11953            "".to_string(),
11954            DiffHunkStatus::Added,
11955            DisplayRow(43)..DisplayRow(44),
11956        ),
11957    ];
11958
11959    multi_buffer_editor.update(cx, |editor, cx| {
11960        let snapshot = editor.snapshot(cx);
11961        let all_hunks = editor_hunks(editor, &snapshot, cx);
11962        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11963        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11964        assert_eq!(all_hunks, expected_all_hunks);
11965        assert_eq!(all_expanded_hunks, Vec::new());
11966    });
11967
11968    multi_buffer_editor.update(cx, |editor, cx| {
11969        editor.select_all(&SelectAll, cx);
11970        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11971    });
11972    cx.executor().run_until_parked();
11973    multi_buffer_editor.update(cx, |editor, cx| {
11974        let snapshot = editor.snapshot(cx);
11975        let all_hunks = editor_hunks(editor, &snapshot, cx);
11976        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11977        assert_eq!(
11978            expanded_hunks_background_highlights(editor, cx),
11979            vec![
11980                DisplayRow(23)..=DisplayRow(23),
11981                DisplayRow(43)..=DisplayRow(43)
11982            ],
11983        );
11984        assert_eq!(all_hunks, expected_all_hunks_shifted);
11985        assert_eq!(all_hunks, all_expanded_hunks);
11986    });
11987
11988    multi_buffer_editor.update(cx, |editor, cx| {
11989        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11990    });
11991    cx.executor().run_until_parked();
11992    multi_buffer_editor.update(cx, |editor, cx| {
11993        let snapshot = editor.snapshot(cx);
11994        let all_hunks = editor_hunks(editor, &snapshot, cx);
11995        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11996        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11997        assert_eq!(all_hunks, expected_all_hunks);
11998        assert_eq!(all_expanded_hunks, Vec::new());
11999    });
12000
12001    multi_buffer_editor.update(cx, |editor, cx| {
12002        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12003    });
12004    cx.executor().run_until_parked();
12005    multi_buffer_editor.update(cx, |editor, cx| {
12006        let snapshot = editor.snapshot(cx);
12007        let all_hunks = editor_hunks(editor, &snapshot, cx);
12008        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12009        assert_eq!(
12010            expanded_hunks_background_highlights(editor, cx),
12011            vec![
12012                DisplayRow(23)..=DisplayRow(23),
12013                DisplayRow(43)..=DisplayRow(43)
12014            ],
12015        );
12016        assert_eq!(all_hunks, expected_all_hunks_shifted);
12017        assert_eq!(all_hunks, all_expanded_hunks);
12018    });
12019
12020    multi_buffer_editor.update(cx, |editor, cx| {
12021        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12022    });
12023    cx.executor().run_until_parked();
12024    multi_buffer_editor.update(cx, |editor, cx| {
12025        let snapshot = editor.snapshot(cx);
12026        let all_hunks = editor_hunks(editor, &snapshot, cx);
12027        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12028        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12029        assert_eq!(all_hunks, expected_all_hunks);
12030        assert_eq!(all_expanded_hunks, Vec::new());
12031    });
12032}
12033
12034#[gpui::test]
12035async fn test_edits_around_toggled_additions(
12036    executor: BackgroundExecutor,
12037    cx: &mut gpui::TestAppContext,
12038) {
12039    init_test(cx, |_| {});
12040
12041    let mut cx = EditorTestContext::new(cx).await;
12042
12043    let diff_base = r#"
12044        use some::mod1;
12045        use some::mod2;
12046
12047        const A: u32 = 42;
12048
12049        fn main() {
12050            println!("hello");
12051
12052            println!("world");
12053        }
12054        "#
12055    .unindent();
12056    executor.run_until_parked();
12057    cx.set_state(
12058        &r#"
12059        use some::mod1;
12060        use some::mod2;
12061
12062        const A: u32 = 42;
12063        const B: u32 = 42;
12064        const C: u32 = 42;
12065        ˇ
12066
12067        fn main() {
12068            println!("hello");
12069
12070            println!("world");
12071        }
12072        "#
12073        .unindent(),
12074    );
12075
12076    cx.set_diff_base(Some(&diff_base));
12077    executor.run_until_parked();
12078    cx.update_editor(|editor, cx| {
12079        let snapshot = editor.snapshot(cx);
12080        let all_hunks = editor_hunks(editor, &snapshot, cx);
12081        assert_eq!(
12082            all_hunks,
12083            vec![(
12084                "".to_string(),
12085                DiffHunkStatus::Added,
12086                DisplayRow(4)..DisplayRow(7)
12087            )]
12088        );
12089    });
12090    cx.update_editor(|editor, cx| {
12091        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12092    });
12093    executor.run_until_parked();
12094    cx.assert_editor_state(
12095        &r#"
12096        use some::mod1;
12097        use some::mod2;
12098
12099        const A: u32 = 42;
12100        const B: u32 = 42;
12101        const C: u32 = 42;
12102        ˇ
12103
12104        fn main() {
12105            println!("hello");
12106
12107            println!("world");
12108        }
12109        "#
12110        .unindent(),
12111    );
12112    cx.update_editor(|editor, cx| {
12113        let snapshot = editor.snapshot(cx);
12114        let all_hunks = editor_hunks(editor, &snapshot, cx);
12115        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12116        assert_eq!(
12117            all_hunks,
12118            vec![(
12119                "".to_string(),
12120                DiffHunkStatus::Added,
12121                DisplayRow(4)..DisplayRow(7)
12122            )]
12123        );
12124        assert_eq!(
12125            expanded_hunks_background_highlights(editor, cx),
12126            vec![DisplayRow(4)..=DisplayRow(6)]
12127        );
12128        assert_eq!(all_hunks, all_expanded_hunks);
12129    });
12130
12131    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12132    executor.run_until_parked();
12133    cx.assert_editor_state(
12134        &r#"
12135        use some::mod1;
12136        use some::mod2;
12137
12138        const A: u32 = 42;
12139        const B: u32 = 42;
12140        const C: u32 = 42;
12141        const D: u32 = 42;
12142        ˇ
12143
12144        fn main() {
12145            println!("hello");
12146
12147            println!("world");
12148        }
12149        "#
12150        .unindent(),
12151    );
12152    cx.update_editor(|editor, cx| {
12153        let snapshot = editor.snapshot(cx);
12154        let all_hunks = editor_hunks(editor, &snapshot, cx);
12155        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12156        assert_eq!(
12157            all_hunks,
12158            vec![(
12159                "".to_string(),
12160                DiffHunkStatus::Added,
12161                DisplayRow(4)..DisplayRow(8)
12162            )]
12163        );
12164        assert_eq!(
12165            expanded_hunks_background_highlights(editor, cx),
12166            vec![DisplayRow(4)..=DisplayRow(6)],
12167            "Edited hunk should have one more line added"
12168        );
12169        assert_eq!(
12170            all_hunks, all_expanded_hunks,
12171            "Expanded hunk should also grow with the addition"
12172        );
12173    });
12174
12175    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12176    executor.run_until_parked();
12177    cx.assert_editor_state(
12178        &r#"
12179        use some::mod1;
12180        use some::mod2;
12181
12182        const A: u32 = 42;
12183        const B: u32 = 42;
12184        const C: u32 = 42;
12185        const D: u32 = 42;
12186        const E: u32 = 42;
12187        ˇ
12188
12189        fn main() {
12190            println!("hello");
12191
12192            println!("world");
12193        }
12194        "#
12195        .unindent(),
12196    );
12197    cx.update_editor(|editor, cx| {
12198        let snapshot = editor.snapshot(cx);
12199        let all_hunks = editor_hunks(editor, &snapshot, cx);
12200        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12201        assert_eq!(
12202            all_hunks,
12203            vec![(
12204                "".to_string(),
12205                DiffHunkStatus::Added,
12206                DisplayRow(4)..DisplayRow(9)
12207            )]
12208        );
12209        assert_eq!(
12210            expanded_hunks_background_highlights(editor, cx),
12211            vec![DisplayRow(4)..=DisplayRow(6)],
12212            "Edited hunk should have one more line added"
12213        );
12214        assert_eq!(all_hunks, all_expanded_hunks);
12215    });
12216
12217    cx.update_editor(|editor, cx| {
12218        editor.move_up(&MoveUp, cx);
12219        editor.delete_line(&DeleteLine, cx);
12220    });
12221    executor.run_until_parked();
12222    cx.assert_editor_state(
12223        &r#"
12224        use some::mod1;
12225        use some::mod2;
12226
12227        const A: u32 = 42;
12228        const B: u32 = 42;
12229        const C: u32 = 42;
12230        const D: u32 = 42;
12231        ˇ
12232
12233        fn main() {
12234            println!("hello");
12235
12236            println!("world");
12237        }
12238        "#
12239        .unindent(),
12240    );
12241    cx.update_editor(|editor, cx| {
12242        let snapshot = editor.snapshot(cx);
12243        let all_hunks = editor_hunks(editor, &snapshot, cx);
12244        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12245        assert_eq!(
12246            all_hunks,
12247            vec![(
12248                "".to_string(),
12249                DiffHunkStatus::Added,
12250                DisplayRow(4)..DisplayRow(8)
12251            )]
12252        );
12253        assert_eq!(
12254            expanded_hunks_background_highlights(editor, cx),
12255            vec![DisplayRow(4)..=DisplayRow(6)],
12256            "Deleting a line should shrint the hunk"
12257        );
12258        assert_eq!(
12259            all_hunks, all_expanded_hunks,
12260            "Expanded hunk should also shrink with the addition"
12261        );
12262    });
12263
12264    cx.update_editor(|editor, 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        editor.move_up(&MoveUp, cx);
12270        editor.delete_line(&DeleteLine, cx);
12271    });
12272    executor.run_until_parked();
12273    cx.assert_editor_state(
12274        &r#"
12275        use some::mod1;
12276        use some::mod2;
12277
12278        const A: u32 = 42;
12279        ˇ
12280
12281        fn main() {
12282            println!("hello");
12283
12284            println!("world");
12285        }
12286        "#
12287        .unindent(),
12288    );
12289    cx.update_editor(|editor, cx| {
12290        let snapshot = editor.snapshot(cx);
12291        let all_hunks = editor_hunks(editor, &snapshot, cx);
12292        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12293        assert_eq!(
12294            all_hunks,
12295            vec![(
12296                "".to_string(),
12297                DiffHunkStatus::Added,
12298                DisplayRow(5)..DisplayRow(6)
12299            )]
12300        );
12301        assert_eq!(
12302            expanded_hunks_background_highlights(editor, cx),
12303            vec![DisplayRow(5)..=DisplayRow(5)]
12304        );
12305        assert_eq!(all_hunks, all_expanded_hunks);
12306    });
12307
12308    cx.update_editor(|editor, cx| {
12309        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12310        editor.delete_line(&DeleteLine, cx);
12311    });
12312    executor.run_until_parked();
12313    cx.assert_editor_state(
12314        &r#"
12315        ˇ
12316
12317        fn main() {
12318            println!("hello");
12319
12320            println!("world");
12321        }
12322        "#
12323        .unindent(),
12324    );
12325    cx.update_editor(|editor, cx| {
12326        let snapshot = editor.snapshot(cx);
12327        let all_hunks = editor_hunks(editor, &snapshot, cx);
12328        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12329        assert_eq!(
12330            all_hunks,
12331            vec![
12332                (
12333                    "use some::mod1;\nuse some::mod2;\n".to_string(),
12334                    DiffHunkStatus::Removed,
12335                    DisplayRow(0)..DisplayRow(0)
12336                ),
12337                (
12338                    "const A: u32 = 42;\n".to_string(),
12339                    DiffHunkStatus::Removed,
12340                    DisplayRow(2)..DisplayRow(2)
12341                )
12342            ]
12343        );
12344        assert_eq!(
12345            expanded_hunks_background_highlights(editor, cx),
12346            Vec::new(),
12347            "Should close all stale expanded addition hunks"
12348        );
12349        assert_eq!(
12350            all_expanded_hunks,
12351            vec![(
12352                "const A: u32 = 42;\n".to_string(),
12353                DiffHunkStatus::Removed,
12354                DisplayRow(2)..DisplayRow(2)
12355            )],
12356            "Should open hunks that were adjacent to the stale addition one"
12357        );
12358    });
12359}
12360
12361#[gpui::test]
12362async fn test_edits_around_toggled_deletions(
12363    executor: BackgroundExecutor,
12364    cx: &mut gpui::TestAppContext,
12365) {
12366    init_test(cx, |_| {});
12367
12368    let mut cx = EditorTestContext::new(cx).await;
12369
12370    let diff_base = r#"
12371        use some::mod1;
12372        use some::mod2;
12373
12374        const A: u32 = 42;
12375        const B: u32 = 42;
12376        const C: u32 = 42;
12377
12378
12379        fn main() {
12380            println!("hello");
12381
12382            println!("world");
12383        }
12384        "#
12385    .unindent();
12386    executor.run_until_parked();
12387    cx.set_state(
12388        &r#"
12389        use some::mod1;
12390        use some::mod2;
12391
12392        ˇconst B: u32 = 42;
12393        const C: u32 = 42;
12394
12395
12396        fn main() {
12397            println!("hello");
12398
12399            println!("world");
12400        }
12401        "#
12402        .unindent(),
12403    );
12404
12405    cx.set_diff_base(Some(&diff_base));
12406    executor.run_until_parked();
12407    cx.update_editor(|editor, cx| {
12408        let snapshot = editor.snapshot(cx);
12409        let all_hunks = editor_hunks(editor, &snapshot, cx);
12410        assert_eq!(
12411            all_hunks,
12412            vec![(
12413                "const A: u32 = 42;\n".to_string(),
12414                DiffHunkStatus::Removed,
12415                DisplayRow(3)..DisplayRow(3)
12416            )]
12417        );
12418    });
12419    cx.update_editor(|editor, cx| {
12420        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12421    });
12422    executor.run_until_parked();
12423    cx.assert_editor_state(
12424        &r#"
12425        use some::mod1;
12426        use some::mod2;
12427
12428        ˇconst B: u32 = 42;
12429        const C: u32 = 42;
12430
12431
12432        fn main() {
12433            println!("hello");
12434
12435            println!("world");
12436        }
12437        "#
12438        .unindent(),
12439    );
12440    cx.update_editor(|editor, cx| {
12441        let snapshot = editor.snapshot(cx);
12442        let all_hunks = editor_hunks(editor, &snapshot, cx);
12443        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12444        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12445        assert_eq!(
12446            all_hunks,
12447            vec![(
12448                "const A: u32 = 42;\n".to_string(),
12449                DiffHunkStatus::Removed,
12450                DisplayRow(4)..DisplayRow(4)
12451            )]
12452        );
12453        assert_eq!(all_hunks, all_expanded_hunks);
12454    });
12455
12456    cx.update_editor(|editor, cx| {
12457        editor.delete_line(&DeleteLine, cx);
12458    });
12459    executor.run_until_parked();
12460    cx.assert_editor_state(
12461        &r#"
12462        use some::mod1;
12463        use some::mod2;
12464
12465        ˇconst C: u32 = 42;
12466
12467
12468        fn main() {
12469            println!("hello");
12470
12471            println!("world");
12472        }
12473        "#
12474        .unindent(),
12475    );
12476    cx.update_editor(|editor, cx| {
12477        let snapshot = editor.snapshot(cx);
12478        let all_hunks = editor_hunks(editor, &snapshot, cx);
12479        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12480        assert_eq!(
12481            expanded_hunks_background_highlights(editor, cx),
12482            Vec::new(),
12483            "Deleted hunks do not highlight current editor's background"
12484        );
12485        assert_eq!(
12486            all_hunks,
12487            vec![(
12488                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12489                DiffHunkStatus::Removed,
12490                DisplayRow(5)..DisplayRow(5)
12491            )]
12492        );
12493        assert_eq!(all_hunks, all_expanded_hunks);
12494    });
12495
12496    cx.update_editor(|editor, cx| {
12497        editor.delete_line(&DeleteLine, cx);
12498    });
12499    executor.run_until_parked();
12500    cx.assert_editor_state(
12501        &r#"
12502        use some::mod1;
12503        use some::mod2;
12504
12505        ˇ
12506
12507        fn main() {
12508            println!("hello");
12509
12510            println!("world");
12511        }
12512        "#
12513        .unindent(),
12514    );
12515    cx.update_editor(|editor, cx| {
12516        let snapshot = editor.snapshot(cx);
12517        let all_hunks = editor_hunks(editor, &snapshot, cx);
12518        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12519        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12520        assert_eq!(
12521            all_hunks,
12522            vec![(
12523                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12524                DiffHunkStatus::Removed,
12525                DisplayRow(6)..DisplayRow(6)
12526            )]
12527        );
12528        assert_eq!(all_hunks, all_expanded_hunks);
12529    });
12530
12531    cx.update_editor(|editor, cx| {
12532        editor.handle_input("replacement", cx);
12533    });
12534    executor.run_until_parked();
12535    cx.assert_editor_state(
12536        &r#"
12537        use some::mod1;
12538        use some::mod2;
12539
12540        replacementˇ
12541
12542        fn main() {
12543            println!("hello");
12544
12545            println!("world");
12546        }
12547        "#
12548        .unindent(),
12549    );
12550    cx.update_editor(|editor, cx| {
12551        let snapshot = editor.snapshot(cx);
12552        let all_hunks = editor_hunks(editor, &snapshot, cx);
12553        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12554        assert_eq!(
12555            all_hunks,
12556            vec![(
12557                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12558                DiffHunkStatus::Modified,
12559                DisplayRow(7)..DisplayRow(8)
12560            )]
12561        );
12562        assert_eq!(
12563            expanded_hunks_background_highlights(editor, cx),
12564            vec![DisplayRow(7)..=DisplayRow(7)],
12565            "Modified expanded hunks should display additions and highlight their background"
12566        );
12567        assert_eq!(all_hunks, all_expanded_hunks);
12568    });
12569}
12570
12571#[gpui::test]
12572async fn test_edits_around_toggled_modifications(
12573    executor: BackgroundExecutor,
12574    cx: &mut gpui::TestAppContext,
12575) {
12576    init_test(cx, |_| {});
12577
12578    let mut cx = EditorTestContext::new(cx).await;
12579
12580    let diff_base = r#"
12581        use some::mod1;
12582        use some::mod2;
12583
12584        const A: u32 = 42;
12585        const B: u32 = 42;
12586        const C: u32 = 42;
12587        const D: u32 = 42;
12588
12589
12590        fn main() {
12591            println!("hello");
12592
12593            println!("world");
12594        }"#
12595    .unindent();
12596    executor.run_until_parked();
12597    cx.set_state(
12598        &r#"
12599        use some::mod1;
12600        use some::mod2;
12601
12602        const A: u32 = 42;
12603        const B: u32 = 42;
12604        const C: u32 = 43ˇ
12605        const D: u32 = 42;
12606
12607
12608        fn main() {
12609            println!("hello");
12610
12611            println!("world");
12612        }"#
12613        .unindent(),
12614    );
12615
12616    cx.set_diff_base(Some(&diff_base));
12617    executor.run_until_parked();
12618    cx.update_editor(|editor, cx| {
12619        let snapshot = editor.snapshot(cx);
12620        let all_hunks = editor_hunks(editor, &snapshot, cx);
12621        assert_eq!(
12622            all_hunks,
12623            vec![(
12624                "const C: u32 = 42;\n".to_string(),
12625                DiffHunkStatus::Modified,
12626                DisplayRow(5)..DisplayRow(6)
12627            )]
12628        );
12629    });
12630    cx.update_editor(|editor, cx| {
12631        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12632    });
12633    executor.run_until_parked();
12634    cx.assert_editor_state(
12635        &r#"
12636        use some::mod1;
12637        use some::mod2;
12638
12639        const A: u32 = 42;
12640        const B: u32 = 42;
12641        const C: u32 = 43ˇ
12642        const D: u32 = 42;
12643
12644
12645        fn main() {
12646            println!("hello");
12647
12648            println!("world");
12649        }"#
12650        .unindent(),
12651    );
12652    cx.update_editor(|editor, cx| {
12653        let snapshot = editor.snapshot(cx);
12654        let all_hunks = editor_hunks(editor, &snapshot, cx);
12655        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12656        assert_eq!(
12657            expanded_hunks_background_highlights(editor, cx),
12658            vec![DisplayRow(6)..=DisplayRow(6)],
12659        );
12660        assert_eq!(
12661            all_hunks,
12662            vec![(
12663                "const C: u32 = 42;\n".to_string(),
12664                DiffHunkStatus::Modified,
12665                DisplayRow(6)..DisplayRow(7)
12666            )]
12667        );
12668        assert_eq!(all_hunks, all_expanded_hunks);
12669    });
12670
12671    cx.update_editor(|editor, cx| {
12672        editor.handle_input("\nnew_line\n", cx);
12673    });
12674    executor.run_until_parked();
12675    cx.assert_editor_state(
12676        &r#"
12677            use some::mod1;
12678            use some::mod2;
12679
12680            const A: u32 = 42;
12681            const B: u32 = 42;
12682            const C: u32 = 43
12683            new_line
12684            ˇ
12685            const D: u32 = 42;
12686
12687
12688            fn main() {
12689                println!("hello");
12690
12691                println!("world");
12692            }"#
12693        .unindent(),
12694    );
12695    cx.update_editor(|editor, cx| {
12696        let snapshot = editor.snapshot(cx);
12697        let all_hunks = editor_hunks(editor, &snapshot, cx);
12698        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12699        assert_eq!(
12700            expanded_hunks_background_highlights(editor, cx),
12701            vec![DisplayRow(6)..=DisplayRow(6)],
12702            "Modified hunk should grow highlighted lines on more text additions"
12703        );
12704        assert_eq!(
12705            all_hunks,
12706            vec![(
12707                "const C: u32 = 42;\n".to_string(),
12708                DiffHunkStatus::Modified,
12709                DisplayRow(6)..DisplayRow(9)
12710            )]
12711        );
12712        assert_eq!(all_hunks, all_expanded_hunks);
12713    });
12714
12715    cx.update_editor(|editor, cx| {
12716        editor.move_up(&MoveUp, cx);
12717        editor.move_up(&MoveUp, cx);
12718        editor.move_up(&MoveUp, cx);
12719        editor.delete_line(&DeleteLine, cx);
12720    });
12721    executor.run_until_parked();
12722    cx.assert_editor_state(
12723        &r#"
12724            use some::mod1;
12725            use some::mod2;
12726
12727            const A: u32 = 42;
12728            ˇconst C: u32 = 43
12729            new_line
12730
12731            const D: u32 = 42;
12732
12733
12734            fn main() {
12735                println!("hello");
12736
12737                println!("world");
12738            }"#
12739        .unindent(),
12740    );
12741    cx.update_editor(|editor, cx| {
12742        let snapshot = editor.snapshot(cx);
12743        let all_hunks = editor_hunks(editor, &snapshot, cx);
12744        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12745        assert_eq!(
12746            expanded_hunks_background_highlights(editor, cx),
12747            vec![DisplayRow(6)..=DisplayRow(8)],
12748        );
12749        assert_eq!(
12750            all_hunks,
12751            vec![(
12752                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12753                DiffHunkStatus::Modified,
12754                DisplayRow(6)..DisplayRow(9)
12755            )],
12756            "Modified hunk should grow deleted lines on text deletions above"
12757        );
12758        assert_eq!(all_hunks, all_expanded_hunks);
12759    });
12760
12761    cx.update_editor(|editor, cx| {
12762        editor.move_up(&MoveUp, cx);
12763        editor.handle_input("v", cx);
12764    });
12765    executor.run_until_parked();
12766    cx.assert_editor_state(
12767        &r#"
12768            use some::mod1;
12769            use some::mod2;
12770
12771            vˇconst A: u32 = 42;
12772            const C: u32 = 43
12773            new_line
12774
12775            const D: u32 = 42;
12776
12777
12778            fn main() {
12779                println!("hello");
12780
12781                println!("world");
12782            }"#
12783        .unindent(),
12784    );
12785    cx.update_editor(|editor, cx| {
12786        let snapshot = editor.snapshot(cx);
12787        let all_hunks = editor_hunks(editor, &snapshot, cx);
12788        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12789        assert_eq!(
12790            expanded_hunks_background_highlights(editor, cx),
12791            vec![DisplayRow(6)..=DisplayRow(9)],
12792            "Modified hunk should grow deleted lines on text modifications above"
12793        );
12794        assert_eq!(
12795            all_hunks,
12796            vec![(
12797                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12798                DiffHunkStatus::Modified,
12799                DisplayRow(6)..DisplayRow(10)
12800            )]
12801        );
12802        assert_eq!(all_hunks, all_expanded_hunks);
12803    });
12804
12805    cx.update_editor(|editor, cx| {
12806        editor.move_down(&MoveDown, cx);
12807        editor.move_down(&MoveDown, cx);
12808        editor.delete_line(&DeleteLine, cx)
12809    });
12810    executor.run_until_parked();
12811    cx.assert_editor_state(
12812        &r#"
12813            use some::mod1;
12814            use some::mod2;
12815
12816            vconst A: u32 = 42;
12817            const C: u32 = 43
12818            ˇ
12819            const D: u32 = 42;
12820
12821
12822            fn main() {
12823                println!("hello");
12824
12825                println!("world");
12826            }"#
12827        .unindent(),
12828    );
12829    cx.update_editor(|editor, cx| {
12830        let snapshot = editor.snapshot(cx);
12831        let all_hunks = editor_hunks(editor, &snapshot, cx);
12832        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12833        assert_eq!(
12834            expanded_hunks_background_highlights(editor, cx),
12835            vec![DisplayRow(6)..=DisplayRow(8)],
12836            "Modified hunk should grow shrink lines on modification lines removal"
12837        );
12838        assert_eq!(
12839            all_hunks,
12840            vec![(
12841                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12842                DiffHunkStatus::Modified,
12843                DisplayRow(6)..DisplayRow(9)
12844            )]
12845        );
12846        assert_eq!(all_hunks, all_expanded_hunks);
12847    });
12848
12849    cx.update_editor(|editor, cx| {
12850        editor.move_up(&MoveUp, cx);
12851        editor.move_up(&MoveUp, cx);
12852        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12853        editor.delete_line(&DeleteLine, cx)
12854    });
12855    executor.run_until_parked();
12856    cx.assert_editor_state(
12857        &r#"
12858            use some::mod1;
12859            use some::mod2;
12860
12861            ˇ
12862
12863            fn main() {
12864                println!("hello");
12865
12866                println!("world");
12867            }"#
12868        .unindent(),
12869    );
12870    cx.update_editor(|editor, cx| {
12871        let snapshot = editor.snapshot(cx);
12872        let all_hunks = editor_hunks(editor, &snapshot, cx);
12873        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12874        assert_eq!(
12875            expanded_hunks_background_highlights(editor, cx),
12876            Vec::new(),
12877            "Modified hunk should turn into a removed one on all modified lines removal"
12878        );
12879        assert_eq!(
12880            all_hunks,
12881            vec![(
12882                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12883                    .to_string(),
12884                DiffHunkStatus::Removed,
12885                DisplayRow(7)..DisplayRow(7)
12886            )]
12887        );
12888        assert_eq!(all_hunks, all_expanded_hunks);
12889    });
12890}
12891
12892#[gpui::test]
12893async fn test_multiple_expanded_hunks_merge(
12894    executor: BackgroundExecutor,
12895    cx: &mut gpui::TestAppContext,
12896) {
12897    init_test(cx, |_| {});
12898
12899    let mut cx = EditorTestContext::new(cx).await;
12900
12901    let diff_base = r#"
12902        use some::mod1;
12903        use some::mod2;
12904
12905        const A: u32 = 42;
12906        const B: u32 = 42;
12907        const C: u32 = 42;
12908        const D: u32 = 42;
12909
12910
12911        fn main() {
12912            println!("hello");
12913
12914            println!("world");
12915        }"#
12916    .unindent();
12917    executor.run_until_parked();
12918    cx.set_state(
12919        &r#"
12920        use some::mod1;
12921        use some::mod2;
12922
12923        const A: u32 = 42;
12924        const B: u32 = 42;
12925        const C: u32 = 43ˇ
12926        const D: u32 = 42;
12927
12928
12929        fn main() {
12930            println!("hello");
12931
12932            println!("world");
12933        }"#
12934        .unindent(),
12935    );
12936
12937    cx.set_diff_base(Some(&diff_base));
12938    executor.run_until_parked();
12939    cx.update_editor(|editor, cx| {
12940        let snapshot = editor.snapshot(cx);
12941        let all_hunks = editor_hunks(editor, &snapshot, cx);
12942        assert_eq!(
12943            all_hunks,
12944            vec![(
12945                "const C: u32 = 42;\n".to_string(),
12946                DiffHunkStatus::Modified,
12947                DisplayRow(5)..DisplayRow(6)
12948            )]
12949        );
12950    });
12951    cx.update_editor(|editor, cx| {
12952        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12953    });
12954    executor.run_until_parked();
12955    cx.assert_editor_state(
12956        &r#"
12957        use some::mod1;
12958        use some::mod2;
12959
12960        const A: u32 = 42;
12961        const B: u32 = 42;
12962        const C: u32 = 43ˇ
12963        const D: u32 = 42;
12964
12965
12966        fn main() {
12967            println!("hello");
12968
12969            println!("world");
12970        }"#
12971        .unindent(),
12972    );
12973    cx.update_editor(|editor, cx| {
12974        let snapshot = editor.snapshot(cx);
12975        let all_hunks = editor_hunks(editor, &snapshot, cx);
12976        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12977        assert_eq!(
12978            expanded_hunks_background_highlights(editor, cx),
12979            vec![DisplayRow(6)..=DisplayRow(6)],
12980        );
12981        assert_eq!(
12982            all_hunks,
12983            vec![(
12984                "const C: u32 = 42;\n".to_string(),
12985                DiffHunkStatus::Modified,
12986                DisplayRow(6)..DisplayRow(7)
12987            )]
12988        );
12989        assert_eq!(all_hunks, all_expanded_hunks);
12990    });
12991
12992    cx.update_editor(|editor, cx| {
12993        editor.handle_input("\nnew_line\n", cx);
12994    });
12995    executor.run_until_parked();
12996    cx.assert_editor_state(
12997        &r#"
12998            use some::mod1;
12999            use some::mod2;
13000
13001            const A: u32 = 42;
13002            const B: u32 = 42;
13003            const C: u32 = 43
13004            new_line
13005            ˇ
13006            const D: u32 = 42;
13007
13008
13009            fn main() {
13010                println!("hello");
13011
13012                println!("world");
13013            }"#
13014        .unindent(),
13015    );
13016}
13017
13018async fn setup_indent_guides_editor(
13019    text: &str,
13020    cx: &mut gpui::TestAppContext,
13021) -> (BufferId, EditorTestContext) {
13022    init_test(cx, |_| {});
13023
13024    let mut cx = EditorTestContext::new(cx).await;
13025
13026    let buffer_id = cx.update_editor(|editor, cx| {
13027        editor.set_text(text, cx);
13028        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13029
13030        buffer_ids[0]
13031    });
13032
13033    (buffer_id, cx)
13034}
13035
13036fn assert_indent_guides(
13037    range: Range<u32>,
13038    expected: Vec<IndentGuide>,
13039    active_indices: Option<Vec<usize>>,
13040    cx: &mut EditorTestContext,
13041) {
13042    let indent_guides = cx.update_editor(|editor, cx| {
13043        let snapshot = editor.snapshot(cx).display_snapshot;
13044        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13045            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13046            true,
13047            &snapshot,
13048            cx,
13049        );
13050
13051        indent_guides.sort_by(|a, b| {
13052            a.depth.cmp(&b.depth).then(
13053                a.start_row
13054                    .cmp(&b.start_row)
13055                    .then(a.end_row.cmp(&b.end_row)),
13056            )
13057        });
13058        indent_guides
13059    });
13060
13061    if let Some(expected) = active_indices {
13062        let active_indices = cx.update_editor(|editor, cx| {
13063            let snapshot = editor.snapshot(cx).display_snapshot;
13064            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13065        });
13066
13067        assert_eq!(
13068            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13069            expected,
13070            "Active indent guide indices do not match"
13071        );
13072    }
13073
13074    let expected: Vec<_> = expected
13075        .into_iter()
13076        .map(|guide| MultiBufferIndentGuide {
13077            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13078            buffer: guide,
13079        })
13080        .collect();
13081
13082    assert_eq!(indent_guides, expected, "Indent guides do not match");
13083}
13084
13085fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13086    IndentGuide {
13087        buffer_id,
13088        start_row,
13089        end_row,
13090        depth,
13091        tab_size: 4,
13092        settings: IndentGuideSettings {
13093            enabled: true,
13094            line_width: 1,
13095            active_line_width: 1,
13096            ..Default::default()
13097        },
13098    }
13099}
13100
13101#[gpui::test]
13102async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13103    let (buffer_id, mut cx) = setup_indent_guides_editor(
13104        &"
13105    fn main() {
13106        let a = 1;
13107    }"
13108        .unindent(),
13109        cx,
13110    )
13111    .await;
13112
13113    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13114}
13115
13116#[gpui::test]
13117async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13118    let (buffer_id, mut cx) = setup_indent_guides_editor(
13119        &"
13120    fn main() {
13121        let a = 1;
13122        let b = 2;
13123    }"
13124        .unindent(),
13125        cx,
13126    )
13127    .await;
13128
13129    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13130}
13131
13132#[gpui::test]
13133async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13134    let (buffer_id, mut cx) = setup_indent_guides_editor(
13135        &"
13136    fn main() {
13137        let a = 1;
13138        if a == 3 {
13139            let b = 2;
13140        } else {
13141            let c = 3;
13142        }
13143    }"
13144        .unindent(),
13145        cx,
13146    )
13147    .await;
13148
13149    assert_indent_guides(
13150        0..8,
13151        vec![
13152            indent_guide(buffer_id, 1, 6, 0),
13153            indent_guide(buffer_id, 3, 3, 1),
13154            indent_guide(buffer_id, 5, 5, 1),
13155        ],
13156        None,
13157        &mut cx,
13158    );
13159}
13160
13161#[gpui::test]
13162async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13163    let (buffer_id, mut cx) = setup_indent_guides_editor(
13164        &"
13165    fn main() {
13166        let a = 1;
13167            let b = 2;
13168        let c = 3;
13169    }"
13170        .unindent(),
13171        cx,
13172    )
13173    .await;
13174
13175    assert_indent_guides(
13176        0..5,
13177        vec![
13178            indent_guide(buffer_id, 1, 3, 0),
13179            indent_guide(buffer_id, 2, 2, 1),
13180        ],
13181        None,
13182        &mut cx,
13183    );
13184}
13185
13186#[gpui::test]
13187async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13188    let (buffer_id, mut cx) = setup_indent_guides_editor(
13189        &"
13190        fn main() {
13191            let a = 1;
13192
13193            let c = 3;
13194        }"
13195        .unindent(),
13196        cx,
13197    )
13198    .await;
13199
13200    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13201}
13202
13203#[gpui::test]
13204async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13205    let (buffer_id, mut cx) = setup_indent_guides_editor(
13206        &"
13207        fn main() {
13208            let a = 1;
13209
13210            let c = 3;
13211
13212            if a == 3 {
13213                let b = 2;
13214            } else {
13215                let c = 3;
13216            }
13217        }"
13218        .unindent(),
13219        cx,
13220    )
13221    .await;
13222
13223    assert_indent_guides(
13224        0..11,
13225        vec![
13226            indent_guide(buffer_id, 1, 9, 0),
13227            indent_guide(buffer_id, 6, 6, 1),
13228            indent_guide(buffer_id, 8, 8, 1),
13229        ],
13230        None,
13231        &mut cx,
13232    );
13233}
13234
13235#[gpui::test]
13236async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13237    let (buffer_id, mut cx) = setup_indent_guides_editor(
13238        &"
13239        fn main() {
13240            let a = 1;
13241
13242            let c = 3;
13243
13244            if a == 3 {
13245                let b = 2;
13246            } else {
13247                let c = 3;
13248            }
13249        }"
13250        .unindent(),
13251        cx,
13252    )
13253    .await;
13254
13255    assert_indent_guides(
13256        1..11,
13257        vec![
13258            indent_guide(buffer_id, 1, 9, 0),
13259            indent_guide(buffer_id, 6, 6, 1),
13260            indent_guide(buffer_id, 8, 8, 1),
13261        ],
13262        None,
13263        &mut cx,
13264    );
13265}
13266
13267#[gpui::test]
13268async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13269    let (buffer_id, mut cx) = setup_indent_guides_editor(
13270        &"
13271        fn main() {
13272            let a = 1;
13273
13274            let c = 3;
13275
13276            if a == 3 {
13277                let b = 2;
13278            } else {
13279                let c = 3;
13280            }
13281        }"
13282        .unindent(),
13283        cx,
13284    )
13285    .await;
13286
13287    assert_indent_guides(
13288        1..10,
13289        vec![
13290            indent_guide(buffer_id, 1, 9, 0),
13291            indent_guide(buffer_id, 6, 6, 1),
13292            indent_guide(buffer_id, 8, 8, 1),
13293        ],
13294        None,
13295        &mut cx,
13296    );
13297}
13298
13299#[gpui::test]
13300async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13301    let (buffer_id, mut cx) = setup_indent_guides_editor(
13302        &"
13303        block1
13304            block2
13305                block3
13306                    block4
13307            block2
13308        block1
13309        block1"
13310            .unindent(),
13311        cx,
13312    )
13313    .await;
13314
13315    assert_indent_guides(
13316        1..10,
13317        vec![
13318            indent_guide(buffer_id, 1, 4, 0),
13319            indent_guide(buffer_id, 2, 3, 1),
13320            indent_guide(buffer_id, 3, 3, 2),
13321        ],
13322        None,
13323        &mut cx,
13324    );
13325}
13326
13327#[gpui::test]
13328async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13329    let (buffer_id, mut cx) = setup_indent_guides_editor(
13330        &"
13331        block1
13332            block2
13333                block3
13334
13335        block1
13336        block1"
13337            .unindent(),
13338        cx,
13339    )
13340    .await;
13341
13342    assert_indent_guides(
13343        0..6,
13344        vec![
13345            indent_guide(buffer_id, 1, 2, 0),
13346            indent_guide(buffer_id, 2, 2, 1),
13347        ],
13348        None,
13349        &mut cx,
13350    );
13351}
13352
13353#[gpui::test]
13354async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13355    let (buffer_id, mut cx) = setup_indent_guides_editor(
13356        &"
13357        block1
13358
13359
13360
13361            block2
13362        "
13363        .unindent(),
13364        cx,
13365    )
13366    .await;
13367
13368    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13369}
13370
13371#[gpui::test]
13372async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13373    let (buffer_id, mut cx) = setup_indent_guides_editor(
13374        &"
13375        def a:
13376        \tb = 3
13377        \tif True:
13378        \t\tc = 4
13379        \t\td = 5
13380        \tprint(b)
13381        "
13382        .unindent(),
13383        cx,
13384    )
13385    .await;
13386
13387    assert_indent_guides(
13388        0..6,
13389        vec![
13390            indent_guide(buffer_id, 1, 6, 0),
13391            indent_guide(buffer_id, 3, 4, 1),
13392        ],
13393        None,
13394        &mut cx,
13395    );
13396}
13397
13398#[gpui::test]
13399async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13400    let (buffer_id, mut cx) = setup_indent_guides_editor(
13401        &"
13402    fn main() {
13403        let a = 1;
13404    }"
13405        .unindent(),
13406        cx,
13407    )
13408    .await;
13409
13410    cx.update_editor(|editor, cx| {
13411        editor.change_selections(None, cx, |s| {
13412            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13413        });
13414    });
13415
13416    assert_indent_guides(
13417        0..3,
13418        vec![indent_guide(buffer_id, 1, 1, 0)],
13419        Some(vec![0]),
13420        &mut cx,
13421    );
13422}
13423
13424#[gpui::test]
13425async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13426    let (buffer_id, mut cx) = setup_indent_guides_editor(
13427        &"
13428    fn main() {
13429        if 1 == 2 {
13430            let a = 1;
13431        }
13432    }"
13433        .unindent(),
13434        cx,
13435    )
13436    .await;
13437
13438    cx.update_editor(|editor, cx| {
13439        editor.change_selections(None, cx, |s| {
13440            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13441        });
13442    });
13443
13444    assert_indent_guides(
13445        0..4,
13446        vec![
13447            indent_guide(buffer_id, 1, 3, 0),
13448            indent_guide(buffer_id, 2, 2, 1),
13449        ],
13450        Some(vec![1]),
13451        &mut cx,
13452    );
13453
13454    cx.update_editor(|editor, cx| {
13455        editor.change_selections(None, cx, |s| {
13456            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13457        });
13458    });
13459
13460    assert_indent_guides(
13461        0..4,
13462        vec![
13463            indent_guide(buffer_id, 1, 3, 0),
13464            indent_guide(buffer_id, 2, 2, 1),
13465        ],
13466        Some(vec![1]),
13467        &mut cx,
13468    );
13469
13470    cx.update_editor(|editor, cx| {
13471        editor.change_selections(None, cx, |s| {
13472            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13473        });
13474    });
13475
13476    assert_indent_guides(
13477        0..4,
13478        vec![
13479            indent_guide(buffer_id, 1, 3, 0),
13480            indent_guide(buffer_id, 2, 2, 1),
13481        ],
13482        Some(vec![0]),
13483        &mut cx,
13484    );
13485}
13486
13487#[gpui::test]
13488async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13489    let (buffer_id, mut cx) = setup_indent_guides_editor(
13490        &"
13491    fn main() {
13492        let a = 1;
13493
13494        let b = 2;
13495    }"
13496        .unindent(),
13497        cx,
13498    )
13499    .await;
13500
13501    cx.update_editor(|editor, cx| {
13502        editor.change_selections(None, cx, |s| {
13503            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13504        });
13505    });
13506
13507    assert_indent_guides(
13508        0..5,
13509        vec![indent_guide(buffer_id, 1, 3, 0)],
13510        Some(vec![0]),
13511        &mut cx,
13512    );
13513}
13514
13515#[gpui::test]
13516async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13517    let (buffer_id, mut cx) = setup_indent_guides_editor(
13518        &"
13519    def m:
13520        a = 1
13521        pass"
13522            .unindent(),
13523        cx,
13524    )
13525    .await;
13526
13527    cx.update_editor(|editor, cx| {
13528        editor.change_selections(None, cx, |s| {
13529            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13530        });
13531    });
13532
13533    assert_indent_guides(
13534        0..3,
13535        vec![indent_guide(buffer_id, 1, 2, 0)],
13536        Some(vec![0]),
13537        &mut cx,
13538    );
13539}
13540
13541#[gpui::test]
13542fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13543    init_test(cx, |_| {});
13544
13545    let editor = cx.add_window(|cx| {
13546        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13547        build_editor(buffer, cx)
13548    });
13549
13550    let render_args = Arc::new(Mutex::new(None));
13551    let snapshot = editor
13552        .update(cx, |editor, cx| {
13553            let snapshot = editor.buffer().read(cx).snapshot(cx);
13554            let range =
13555                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13556
13557            struct RenderArgs {
13558                row: MultiBufferRow,
13559                folded: bool,
13560                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13561            }
13562
13563            let crease = Crease::new(
13564                range,
13565                FoldPlaceholder::test(),
13566                {
13567                    let toggle_callback = render_args.clone();
13568                    move |row, folded, callback, _cx| {
13569                        *toggle_callback.lock() = Some(RenderArgs {
13570                            row,
13571                            folded,
13572                            callback,
13573                        });
13574                        div()
13575                    }
13576                },
13577                |_row, _folded, _cx| div(),
13578            );
13579
13580            editor.insert_creases(Some(crease), cx);
13581            let snapshot = editor.snapshot(cx);
13582            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13583            snapshot
13584        })
13585        .unwrap();
13586
13587    let render_args = render_args.lock().take().unwrap();
13588    assert_eq!(render_args.row, MultiBufferRow(1));
13589    assert!(!render_args.folded);
13590    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13591
13592    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13593        .unwrap();
13594    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13595    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13596
13597    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13598        .unwrap();
13599    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13600    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13601}
13602
13603#[gpui::test]
13604async fn test_input_text(cx: &mut gpui::TestAppContext) {
13605    init_test(cx, |_| {});
13606    let mut cx = EditorTestContext::new(cx).await;
13607
13608    cx.set_state(
13609        &r#"ˇone
13610        two
13611
13612        three
13613        fourˇ
13614        five
13615
13616        siˇx"#
13617            .unindent(),
13618    );
13619
13620    cx.dispatch_action(HandleInput(String::new()));
13621    cx.assert_editor_state(
13622        &r#"ˇone
13623        two
13624
13625        three
13626        fourˇ
13627        five
13628
13629        siˇx"#
13630            .unindent(),
13631    );
13632
13633    cx.dispatch_action(HandleInput("AAAA".to_string()));
13634    cx.assert_editor_state(
13635        &r#"AAAAˇone
13636        two
13637
13638        three
13639        fourAAAAˇ
13640        five
13641
13642        siAAAAˇx"#
13643            .unindent(),
13644    );
13645}
13646
13647#[gpui::test]
13648async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13649    init_test(cx, |_| {});
13650
13651    let mut cx = EditorTestContext::new(cx).await;
13652    cx.set_state(
13653        r#"let foo = 1;
13654let foo = 2;
13655let foo = 3;
13656let fooˇ = 4;
13657let foo = 5;
13658let foo = 6;
13659let foo = 7;
13660let foo = 8;
13661let foo = 9;
13662let foo = 10;
13663let foo = 11;
13664let foo = 12;
13665let foo = 13;
13666let foo = 14;
13667let foo = 15;"#,
13668    );
13669
13670    cx.update_editor(|e, cx| {
13671        assert_eq!(
13672            e.next_scroll_position,
13673            NextScrollCursorCenterTopBottom::Center,
13674            "Default next scroll direction is center",
13675        );
13676
13677        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13678        assert_eq!(
13679            e.next_scroll_position,
13680            NextScrollCursorCenterTopBottom::Top,
13681            "After center, next scroll direction should be top",
13682        );
13683
13684        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13685        assert_eq!(
13686            e.next_scroll_position,
13687            NextScrollCursorCenterTopBottom::Bottom,
13688            "After top, next scroll direction should be bottom",
13689        );
13690
13691        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13692        assert_eq!(
13693            e.next_scroll_position,
13694            NextScrollCursorCenterTopBottom::Center,
13695            "After bottom, scrolling should start over",
13696        );
13697
13698        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13699        assert_eq!(
13700            e.next_scroll_position,
13701            NextScrollCursorCenterTopBottom::Top,
13702            "Scrolling continues if retriggered fast enough"
13703        );
13704    });
13705
13706    cx.executor()
13707        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13708    cx.executor().run_until_parked();
13709    cx.update_editor(|e, _| {
13710        assert_eq!(
13711            e.next_scroll_position,
13712            NextScrollCursorCenterTopBottom::Center,
13713            "If scrolling is not triggered fast enough, it should reset"
13714        );
13715    });
13716}
13717
13718#[gpui::test]
13719async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13720    init_test(cx, |_| {});
13721    let mut cx = EditorLspTestContext::new_rust(
13722        lsp::ServerCapabilities {
13723            definition_provider: Some(lsp::OneOf::Left(true)),
13724            references_provider: Some(lsp::OneOf::Left(true)),
13725            ..lsp::ServerCapabilities::default()
13726        },
13727        cx,
13728    )
13729    .await;
13730
13731    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13732        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13733            move |params, _| async move {
13734                if empty_go_to_definition {
13735                    Ok(None)
13736                } else {
13737                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13738                        uri: params.text_document_position_params.text_document.uri,
13739                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13740                    })))
13741                }
13742            },
13743        );
13744        let references =
13745            cx.lsp
13746                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13747                    Ok(Some(vec![lsp::Location {
13748                        uri: params.text_document_position.text_document.uri,
13749                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13750                    }]))
13751                });
13752        (go_to_definition, references)
13753    };
13754
13755    cx.set_state(
13756        &r#"fn one() {
13757            let mut a = ˇtwo();
13758        }
13759
13760        fn two() {}"#
13761            .unindent(),
13762    );
13763    set_up_lsp_handlers(false, &mut cx);
13764    let navigated = cx
13765        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13766        .await
13767        .expect("Failed to navigate to definition");
13768    assert_eq!(
13769        navigated,
13770        Navigated::Yes,
13771        "Should have navigated to definition from the GetDefinition response"
13772    );
13773    cx.assert_editor_state(
13774        &r#"fn one() {
13775            let mut a = two();
13776        }
13777
13778        fn «twoˇ»() {}"#
13779            .unindent(),
13780    );
13781
13782    let editors = cx.update_workspace(|workspace, cx| {
13783        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13784    });
13785    cx.update_editor(|_, test_editor_cx| {
13786        assert_eq!(
13787            editors.len(),
13788            1,
13789            "Initially, only one, test, editor should be open in the workspace"
13790        );
13791        assert_eq!(
13792            test_editor_cx.view(),
13793            editors.last().expect("Asserted len is 1")
13794        );
13795    });
13796
13797    set_up_lsp_handlers(true, &mut cx);
13798    let navigated = cx
13799        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13800        .await
13801        .expect("Failed to navigate to lookup references");
13802    assert_eq!(
13803        navigated,
13804        Navigated::Yes,
13805        "Should have navigated to references as a fallback after empty GoToDefinition response"
13806    );
13807    // We should not change the selections in the existing file,
13808    // if opening another milti buffer with the references
13809    cx.assert_editor_state(
13810        &r#"fn one() {
13811            let mut a = two();
13812        }
13813
13814        fn «twoˇ»() {}"#
13815            .unindent(),
13816    );
13817    let editors = cx.update_workspace(|workspace, cx| {
13818        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13819    });
13820    cx.update_editor(|_, test_editor_cx| {
13821        assert_eq!(
13822            editors.len(),
13823            2,
13824            "After falling back to references search, we open a new editor with the results"
13825        );
13826        let references_fallback_text = editors
13827            .into_iter()
13828            .find(|new_editor| new_editor != test_editor_cx.view())
13829            .expect("Should have one non-test editor now")
13830            .read(test_editor_cx)
13831            .text(test_editor_cx);
13832        assert_eq!(
13833            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13834            "Should use the range from the references response and not the GoToDefinition one"
13835        );
13836    });
13837}
13838
13839fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13840    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13841    point..point
13842}
13843
13844fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13845    let (text, ranges) = marked_text_ranges(marked_text, true);
13846    assert_eq!(view.text(cx), text);
13847    assert_eq!(
13848        view.selections.ranges(cx),
13849        ranges,
13850        "Assert selections are {}",
13851        marked_text
13852    );
13853}
13854
13855pub fn handle_signature_help_request(
13856    cx: &mut EditorLspTestContext,
13857    mocked_response: lsp::SignatureHelp,
13858) -> impl Future<Output = ()> {
13859    let mut request =
13860        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13861            let mocked_response = mocked_response.clone();
13862            async move { Ok(Some(mocked_response)) }
13863        });
13864
13865    async move {
13866        request.next().await;
13867    }
13868}
13869
13870/// Handle completion request passing a marked string specifying where the completion
13871/// should be triggered from using '|' character, what range should be replaced, and what completions
13872/// should be returned using '<' and '>' to delimit the range
13873pub fn handle_completion_request(
13874    cx: &mut EditorLspTestContext,
13875    marked_string: &str,
13876    completions: Vec<&'static str>,
13877    counter: Arc<AtomicUsize>,
13878) -> impl Future<Output = ()> {
13879    let complete_from_marker: TextRangeMarker = '|'.into();
13880    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13881    let (_, mut marked_ranges) = marked_text_ranges_by(
13882        marked_string,
13883        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13884    );
13885
13886    let complete_from_position =
13887        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13888    let replace_range =
13889        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13890
13891    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13892        let completions = completions.clone();
13893        counter.fetch_add(1, atomic::Ordering::Release);
13894        async move {
13895            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13896            assert_eq!(
13897                params.text_document_position.position,
13898                complete_from_position
13899            );
13900            Ok(Some(lsp::CompletionResponse::Array(
13901                completions
13902                    .iter()
13903                    .map(|completion_text| lsp::CompletionItem {
13904                        label: completion_text.to_string(),
13905                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13906                            range: replace_range,
13907                            new_text: completion_text.to_string(),
13908                        })),
13909                        ..Default::default()
13910                    })
13911                    .collect(),
13912            )))
13913        }
13914    });
13915
13916    async move {
13917        request.next().await;
13918    }
13919}
13920
13921fn handle_resolve_completion_request(
13922    cx: &mut EditorLspTestContext,
13923    edits: Option<Vec<(&'static str, &'static str)>>,
13924) -> impl Future<Output = ()> {
13925    let edits = edits.map(|edits| {
13926        edits
13927            .iter()
13928            .map(|(marked_string, new_text)| {
13929                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13930                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13931                lsp::TextEdit::new(replace_range, new_text.to_string())
13932            })
13933            .collect::<Vec<_>>()
13934    });
13935
13936    let mut request =
13937        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13938            let edits = edits.clone();
13939            async move {
13940                Ok(lsp::CompletionItem {
13941                    additional_text_edits: edits,
13942                    ..Default::default()
13943                })
13944            }
13945        });
13946
13947    async move {
13948        request.next().await;
13949    }
13950}
13951
13952pub(crate) fn update_test_language_settings(
13953    cx: &mut TestAppContext,
13954    f: impl Fn(&mut AllLanguageSettingsContent),
13955) {
13956    cx.update(|cx| {
13957        SettingsStore::update_global(cx, |store, cx| {
13958            store.update_user_settings::<AllLanguageSettings>(cx, f);
13959        });
13960    });
13961}
13962
13963pub(crate) fn update_test_project_settings(
13964    cx: &mut TestAppContext,
13965    f: impl Fn(&mut ProjectSettings),
13966) {
13967    cx.update(|cx| {
13968        SettingsStore::update_global(cx, |store, cx| {
13969            store.update_user_settings::<ProjectSettings>(cx, f);
13970        });
13971    });
13972}
13973
13974pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13975    cx.update(|cx| {
13976        assets::Assets.load_test_fonts(cx);
13977        let store = SettingsStore::test(cx);
13978        cx.set_global(store);
13979        theme::init(theme::LoadThemes::JustBase, cx);
13980        release_channel::init(SemanticVersion::default(), cx);
13981        client::init_settings(cx);
13982        language::init(cx);
13983        Project::init_settings(cx);
13984        workspace::init_settings(cx);
13985        crate::init(cx);
13986    });
13987
13988    update_test_language_settings(cx, f);
13989}
13990
13991pub(crate) fn rust_lang() -> Arc<Language> {
13992    Arc::new(Language::new(
13993        LanguageConfig {
13994            name: "Rust".into(),
13995            matcher: LanguageMatcher {
13996                path_suffixes: vec!["rs".to_string()],
13997                ..Default::default()
13998            },
13999            ..Default::default()
14000        },
14001        Some(tree_sitter_rust::LANGUAGE.into()),
14002    ))
14003}
14004
14005#[track_caller]
14006fn assert_hunk_revert(
14007    not_reverted_text_with_selections: &str,
14008    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14009    expected_reverted_text_with_selections: &str,
14010    base_text: &str,
14011    cx: &mut EditorLspTestContext,
14012) {
14013    cx.set_state(not_reverted_text_with_selections);
14014    cx.update_editor(|editor, cx| {
14015        editor
14016            .buffer()
14017            .read(cx)
14018            .as_singleton()
14019            .unwrap()
14020            .update(cx, |buffer, cx| {
14021                buffer.set_diff_base(Some(base_text.into()), cx);
14022            });
14023    });
14024    cx.executor().run_until_parked();
14025
14026    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14027        let snapshot = editor.buffer().read(cx).snapshot(cx);
14028        let reverted_hunk_statuses = snapshot
14029            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
14030            .map(|hunk| hunk_status(&hunk))
14031            .collect::<Vec<_>>();
14032
14033        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14034        reverted_hunk_statuses
14035    });
14036    cx.executor().run_until_parked();
14037    cx.assert_editor_state(expected_reverted_text_with_selections);
14038    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14039}