editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671#[gpui::test]
 3672async fn test_fold_perf(cx: &mut TestAppContext) {
 3673    use std::fmt::Write;
 3674    init_test(cx, |_| {});
 3675    let mut view = EditorTestContext::new(cx).await;
 3676    let language_registry = view.language_registry();
 3677    let language_name = Arc::from("Markdown");
 3678    let md_language = Arc::new(
 3679        Language::new(
 3680            LanguageConfig {
 3681                name: Arc::clone(&language_name),
 3682                matcher: LanguageMatcher {
 3683                    path_suffixes: vec!["md".to_string()],
 3684                    ..Default::default()
 3685                },
 3686                ..Default::default()
 3687            },
 3688            Some(tree_sitter_md::language()),
 3689        )
 3690        .with_highlights_query(
 3691            r#"
 3692        "#,
 3693        )
 3694        .unwrap(),
 3695    );
 3696    language_registry.add(md_language.clone());
 3697
 3698    let mut text = String::default();
 3699    writeln!(&mut text, "start").unwrap();
 3700    writeln!(&mut text, "```").unwrap();
 3701    const LINE_COUNT: u32 = 10000;
 3702    for i in 0..LINE_COUNT {
 3703        writeln!(&mut text, "{i}").unwrap();
 3704    }
 3705
 3706    writeln!(&mut text, "```").unwrap();
 3707    writeln!(&mut text, "end").unwrap();
 3708    view.update_buffer(|buffer, cx| {
 3709        buffer.set_language(Some(md_language), cx);
 3710    });
 3711    let t0 = Instant::now();
 3712    _ = view.update_editor(|view, cx| {
 3713        eprintln!("Text length: {}", text.len());
 3714        view.set_text(text, cx);
 3715        eprintln!(">>");
 3716        view.fold_ranges(
 3717            vec![(
 3718                Point::new(1, 0)..Point::new(LINE_COUNT + 2, 3),
 3719                FoldPlaceholder::test(),
 3720            )],
 3721            false,
 3722            cx,
 3723        );
 3724    });
 3725    eprintln!("{:?}", t0.elapsed());
 3726    eprintln!("<<");
 3727}
 3728
 3729#[gpui::test]
 3730fn test_move_line_up_down(cx: &mut TestAppContext) {
 3731    init_test(cx, |_| {});
 3732
 3733    let view = cx.add_window(|cx| {
 3734        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3735        build_editor(buffer, cx)
 3736    });
 3737    _ = view.update(cx, |view, cx| {
 3738        view.fold_ranges(
 3739            vec![
 3740                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3741                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3742                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3743            ],
 3744            true,
 3745            cx,
 3746        );
 3747        view.change_selections(None, cx, |s| {
 3748            s.select_display_ranges([
 3749                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3750                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3751                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3752                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3753            ])
 3754        });
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3758        );
 3759
 3760        view.move_line_up(&MoveLineUp, cx);
 3761        assert_eq!(
 3762            view.display_text(cx),
 3763            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3764        );
 3765        assert_eq!(
 3766            view.selections.display_ranges(cx),
 3767            vec![
 3768                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3769                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3770                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3771                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3772            ]
 3773        );
 3774    });
 3775
 3776    _ = view.update(cx, |view, cx| {
 3777        view.move_line_down(&MoveLineDown, cx);
 3778        assert_eq!(
 3779            view.display_text(cx),
 3780            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3781        );
 3782        assert_eq!(
 3783            view.selections.display_ranges(cx),
 3784            vec![
 3785                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3786                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3787                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3788                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3789            ]
 3790        );
 3791    });
 3792
 3793    _ = view.update(cx, |view, cx| {
 3794        view.move_line_down(&MoveLineDown, cx);
 3795        assert_eq!(
 3796            view.display_text(cx),
 3797            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3798        );
 3799        assert_eq!(
 3800            view.selections.display_ranges(cx),
 3801            vec![
 3802                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3803                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3804                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3805                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3806            ]
 3807        );
 3808    });
 3809
 3810    _ = view.update(cx, |view, cx| {
 3811        view.move_line_up(&MoveLineUp, cx);
 3812        assert_eq!(
 3813            view.display_text(cx),
 3814            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3815        );
 3816        assert_eq!(
 3817            view.selections.display_ranges(cx),
 3818            vec![
 3819                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3820                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3821                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3822                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3823            ]
 3824        );
 3825    });
 3826}
 3827
 3828#[gpui::test]
 3829fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3830    init_test(cx, |_| {});
 3831
 3832    let editor = cx.add_window(|cx| {
 3833        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3834        build_editor(buffer, cx)
 3835    });
 3836    _ = editor.update(cx, |editor, cx| {
 3837        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3838        editor.insert_blocks(
 3839            [BlockProperties {
 3840                style: BlockStyle::Fixed,
 3841                position: snapshot.anchor_after(Point::new(2, 0)),
 3842                disposition: BlockDisposition::Below,
 3843                height: 1,
 3844                render: Box::new(|_| div().into_any()),
 3845            }],
 3846            Some(Autoscroll::fit()),
 3847            cx,
 3848        );
 3849        editor.change_selections(None, cx, |s| {
 3850            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3851        });
 3852        editor.move_line_down(&MoveLineDown, cx);
 3853    });
 3854}
 3855
 3856#[gpui::test]
 3857fn test_transpose(cx: &mut TestAppContext) {
 3858    init_test(cx, |_| {});
 3859
 3860    _ = cx.add_window(|cx| {
 3861        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3862        editor.set_style(EditorStyle::default(), cx);
 3863        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3864        editor.transpose(&Default::default(), cx);
 3865        assert_eq!(editor.text(cx), "bac");
 3866        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3867
 3868        editor.transpose(&Default::default(), cx);
 3869        assert_eq!(editor.text(cx), "bca");
 3870        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3871
 3872        editor.transpose(&Default::default(), cx);
 3873        assert_eq!(editor.text(cx), "bac");
 3874        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3875
 3876        editor
 3877    });
 3878
 3879    _ = cx.add_window(|cx| {
 3880        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3881        editor.set_style(EditorStyle::default(), cx);
 3882        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3883        editor.transpose(&Default::default(), cx);
 3884        assert_eq!(editor.text(cx), "acb\nde");
 3885        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3886
 3887        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3888        editor.transpose(&Default::default(), cx);
 3889        assert_eq!(editor.text(cx), "acbd\ne");
 3890        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3891
 3892        editor.transpose(&Default::default(), cx);
 3893        assert_eq!(editor.text(cx), "acbde\n");
 3894        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3895
 3896        editor.transpose(&Default::default(), cx);
 3897        assert_eq!(editor.text(cx), "acbd\ne");
 3898        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3899
 3900        editor
 3901    });
 3902
 3903    _ = cx.add_window(|cx| {
 3904        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3905        editor.set_style(EditorStyle::default(), cx);
 3906        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3907        editor.transpose(&Default::default(), cx);
 3908        assert_eq!(editor.text(cx), "bacd\ne");
 3909        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3910
 3911        editor.transpose(&Default::default(), cx);
 3912        assert_eq!(editor.text(cx), "bcade\n");
 3913        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3914
 3915        editor.transpose(&Default::default(), cx);
 3916        assert_eq!(editor.text(cx), "bcda\ne");
 3917        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3918
 3919        editor.transpose(&Default::default(), cx);
 3920        assert_eq!(editor.text(cx), "bcade\n");
 3921        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3922
 3923        editor.transpose(&Default::default(), cx);
 3924        assert_eq!(editor.text(cx), "bcaed\n");
 3925        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3926
 3927        editor
 3928    });
 3929
 3930    _ = cx.add_window(|cx| {
 3931        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3932        editor.set_style(EditorStyle::default(), cx);
 3933        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3934        editor.transpose(&Default::default(), cx);
 3935        assert_eq!(editor.text(cx), "🏀🍐✋");
 3936        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3937
 3938        editor.transpose(&Default::default(), cx);
 3939        assert_eq!(editor.text(cx), "🏀✋🍐");
 3940        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3941
 3942        editor.transpose(&Default::default(), cx);
 3943        assert_eq!(editor.text(cx), "🏀🍐✋");
 3944        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3945
 3946        editor
 3947    });
 3948}
 3949
 3950#[gpui::test]
 3951async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3952    init_test(cx, |_| {});
 3953
 3954    let mut cx = EditorTestContext::new(cx).await;
 3955
 3956    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3957    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3958    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3959
 3960    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3961    cx.set_state("two ˇfour ˇsix ˇ");
 3962    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3963    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3964
 3965    // Paste again but with only two cursors. Since the number of cursors doesn't
 3966    // match the number of slices in the clipboard, the entire clipboard text
 3967    // is pasted at each cursor.
 3968    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3969    cx.update_editor(|e, cx| {
 3970        e.handle_input("( ", cx);
 3971        e.paste(&Paste, cx);
 3972        e.handle_input(") ", cx);
 3973    });
 3974    cx.assert_editor_state(
 3975        &([
 3976            "( one✅ ",
 3977            "three ",
 3978            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3979            "three ",
 3980            "five ) ˇ",
 3981        ]
 3982        .join("\n")),
 3983    );
 3984
 3985    // Cut with three selections, one of which is full-line.
 3986    cx.set_state(indoc! {"
 3987        1«2ˇ»3
 3988        4ˇ567
 3989        «8ˇ»9"});
 3990    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3991    cx.assert_editor_state(indoc! {"
 3992        1ˇ3
 3993        ˇ9"});
 3994
 3995    // Paste with three selections, noticing how the copied selection that was full-line
 3996    // gets inserted before the second cursor.
 3997    cx.set_state(indoc! {"
 3998        1ˇ3
 3999 4000        «oˇ»ne"});
 4001    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4002    cx.assert_editor_state(indoc! {"
 4003        12ˇ3
 4004        4567
 4005 4006        8ˇne"});
 4007
 4008    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4009    cx.set_state(indoc! {"
 4010        The quick brown
 4011        fox juˇmps over
 4012        the lazy dog"});
 4013    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4014    assert_eq!(
 4015        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 4016        Some("fox jumps over\n".to_owned())
 4017    );
 4018
 4019    // Paste with three selections, noticing how the copied full-line selection is inserted
 4020    // before the empty selections but replaces the selection that is non-empty.
 4021    cx.set_state(indoc! {"
 4022        Tˇhe quick brown
 4023        «foˇ»x jumps over
 4024        tˇhe lazy dog"});
 4025    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4026    cx.assert_editor_state(indoc! {"
 4027        fox jumps over
 4028        Tˇhe quick brown
 4029        fox jumps over
 4030        ˇx jumps over
 4031        fox jumps over
 4032        tˇhe lazy dog"});
 4033}
 4034
 4035#[gpui::test]
 4036async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4037    init_test(cx, |_| {});
 4038
 4039    let mut cx = EditorTestContext::new(cx).await;
 4040    let language = Arc::new(Language::new(
 4041        LanguageConfig::default(),
 4042        Some(tree_sitter_rust::language()),
 4043    ));
 4044    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4045
 4046    // Cut an indented block, without the leading whitespace.
 4047    cx.set_state(indoc! {"
 4048        const a: B = (
 4049            c(),
 4050            «d(
 4051                e,
 4052                f
 4053            )ˇ»
 4054        );
 4055    "});
 4056    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4057    cx.assert_editor_state(indoc! {"
 4058        const a: B = (
 4059            c(),
 4060            ˇ
 4061        );
 4062    "});
 4063
 4064    // Paste it at the same position.
 4065    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4066    cx.assert_editor_state(indoc! {"
 4067        const a: B = (
 4068            c(),
 4069            d(
 4070                e,
 4071                f
 4072 4073        );
 4074    "});
 4075
 4076    // Paste it at a line with a lower indent level.
 4077    cx.set_state(indoc! {"
 4078        ˇ
 4079        const a: B = (
 4080            c(),
 4081        );
 4082    "});
 4083    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4084    cx.assert_editor_state(indoc! {"
 4085        d(
 4086            e,
 4087            f
 4088 4089        const a: B = (
 4090            c(),
 4091        );
 4092    "});
 4093
 4094    // Cut an indented block, with the leading whitespace.
 4095    cx.set_state(indoc! {"
 4096        const a: B = (
 4097            c(),
 4098        «    d(
 4099                e,
 4100                f
 4101            )
 4102        ˇ»);
 4103    "});
 4104    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4105    cx.assert_editor_state(indoc! {"
 4106        const a: B = (
 4107            c(),
 4108        ˇ);
 4109    "});
 4110
 4111    // Paste it at the same position.
 4112    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4113    cx.assert_editor_state(indoc! {"
 4114        const a: B = (
 4115            c(),
 4116            d(
 4117                e,
 4118                f
 4119            )
 4120        ˇ);
 4121    "});
 4122
 4123    // Paste it at a line with a higher indent level.
 4124    cx.set_state(indoc! {"
 4125        const a: B = (
 4126            c(),
 4127            d(
 4128                e,
 4129 4130            )
 4131        );
 4132    "});
 4133    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4134    cx.assert_editor_state(indoc! {"
 4135        const a: B = (
 4136            c(),
 4137            d(
 4138                e,
 4139                f    d(
 4140                    e,
 4141                    f
 4142                )
 4143        ˇ
 4144            )
 4145        );
 4146    "});
 4147}
 4148
 4149#[gpui::test]
 4150fn test_select_all(cx: &mut TestAppContext) {
 4151    init_test(cx, |_| {});
 4152
 4153    let view = cx.add_window(|cx| {
 4154        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4155        build_editor(buffer, cx)
 4156    });
 4157    _ = view.update(cx, |view, cx| {
 4158        view.select_all(&SelectAll, cx);
 4159        assert_eq!(
 4160            view.selections.display_ranges(cx),
 4161            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4162        );
 4163    });
 4164}
 4165
 4166#[gpui::test]
 4167fn test_select_line(cx: &mut TestAppContext) {
 4168    init_test(cx, |_| {});
 4169
 4170    let view = cx.add_window(|cx| {
 4171        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4172        build_editor(buffer, cx)
 4173    });
 4174    _ = view.update(cx, |view, cx| {
 4175        view.change_selections(None, cx, |s| {
 4176            s.select_display_ranges([
 4177                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4178                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4179                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4180                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4181            ])
 4182        });
 4183        view.select_line(&SelectLine, cx);
 4184        assert_eq!(
 4185            view.selections.display_ranges(cx),
 4186            vec![
 4187                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4188                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4189            ]
 4190        );
 4191    });
 4192
 4193    _ = view.update(cx, |view, cx| {
 4194        view.select_line(&SelectLine, cx);
 4195        assert_eq!(
 4196            view.selections.display_ranges(cx),
 4197            vec![
 4198                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4199                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4200            ]
 4201        );
 4202    });
 4203
 4204    _ = view.update(cx, |view, cx| {
 4205        view.select_line(&SelectLine, cx);
 4206        assert_eq!(
 4207            view.selections.display_ranges(cx),
 4208            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4209        );
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let view = cx.add_window(|cx| {
 4218        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4219        build_editor(buffer, cx)
 4220    });
 4221    _ = view.update(cx, |view, cx| {
 4222        view.fold_ranges(
 4223            vec![
 4224                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4225                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4226                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4227            ],
 4228            true,
 4229            cx,
 4230        );
 4231        view.change_selections(None, cx, |s| {
 4232            s.select_display_ranges([
 4233                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4234                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4235                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4236                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4237            ])
 4238        });
 4239        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4240    });
 4241
 4242    _ = view.update(cx, |view, cx| {
 4243        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4244        assert_eq!(
 4245            view.display_text(cx),
 4246            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4247        );
 4248        assert_eq!(
 4249            view.selections.display_ranges(cx),
 4250            [
 4251                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4252                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4253                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4254                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4255            ]
 4256        );
 4257    });
 4258
 4259    _ = view.update(cx, |view, cx| {
 4260        view.change_selections(None, cx, |s| {
 4261            s.select_display_ranges([
 4262                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4263            ])
 4264        });
 4265        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4266        assert_eq!(
 4267            view.display_text(cx),
 4268            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4269        );
 4270        assert_eq!(
 4271            view.selections.display_ranges(cx),
 4272            [
 4273                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4274                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4275                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4276                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4277                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4278                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4279                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4280                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4281            ]
 4282        );
 4283    });
 4284}
 4285
 4286#[gpui::test]
 4287async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4288    init_test(cx, |_| {});
 4289
 4290    let mut cx = EditorTestContext::new(cx).await;
 4291
 4292    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4293    cx.set_state(indoc!(
 4294        r#"abc
 4295           defˇghi
 4296
 4297           jk
 4298           nlmo
 4299           "#
 4300    ));
 4301
 4302    cx.update_editor(|editor, cx| {
 4303        editor.add_selection_above(&Default::default(), cx);
 4304    });
 4305
 4306    cx.assert_editor_state(indoc!(
 4307        r#"abcˇ
 4308           defˇghi
 4309
 4310           jk
 4311           nlmo
 4312           "#
 4313    ));
 4314
 4315    cx.update_editor(|editor, cx| {
 4316        editor.add_selection_above(&Default::default(), cx);
 4317    });
 4318
 4319    cx.assert_editor_state(indoc!(
 4320        r#"abcˇ
 4321            defˇghi
 4322
 4323            jk
 4324            nlmo
 4325            "#
 4326    ));
 4327
 4328    cx.update_editor(|view, cx| {
 4329        view.add_selection_below(&Default::default(), cx);
 4330    });
 4331
 4332    cx.assert_editor_state(indoc!(
 4333        r#"abc
 4334           defˇghi
 4335
 4336           jk
 4337           nlmo
 4338           "#
 4339    ));
 4340
 4341    cx.update_editor(|view, cx| {
 4342        view.undo_selection(&Default::default(), cx);
 4343    });
 4344
 4345    cx.assert_editor_state(indoc!(
 4346        r#"abcˇ
 4347           defˇghi
 4348
 4349           jk
 4350           nlmo
 4351           "#
 4352    ));
 4353
 4354    cx.update_editor(|view, cx| {
 4355        view.redo_selection(&Default::default(), cx);
 4356    });
 4357
 4358    cx.assert_editor_state(indoc!(
 4359        r#"abc
 4360           defˇghi
 4361
 4362           jk
 4363           nlmo
 4364           "#
 4365    ));
 4366
 4367    cx.update_editor(|view, cx| {
 4368        view.add_selection_below(&Default::default(), cx);
 4369    });
 4370
 4371    cx.assert_editor_state(indoc!(
 4372        r#"abc
 4373           defˇghi
 4374
 4375           jk
 4376           nlmˇo
 4377           "#
 4378    ));
 4379
 4380    cx.update_editor(|view, cx| {
 4381        view.add_selection_below(&Default::default(), cx);
 4382    });
 4383
 4384    cx.assert_editor_state(indoc!(
 4385        r#"abc
 4386           defˇghi
 4387
 4388           jk
 4389           nlmˇo
 4390           "#
 4391    ));
 4392
 4393    // change selections
 4394    cx.set_state(indoc!(
 4395        r#"abc
 4396           def«ˇg»hi
 4397
 4398           jk
 4399           nlmo
 4400           "#
 4401    ));
 4402
 4403    cx.update_editor(|view, cx| {
 4404        view.add_selection_below(&Default::default(), cx);
 4405    });
 4406
 4407    cx.assert_editor_state(indoc!(
 4408        r#"abc
 4409           def«ˇg»hi
 4410
 4411           jk
 4412           nlm«ˇo»
 4413           "#
 4414    ));
 4415
 4416    cx.update_editor(|view, cx| {
 4417        view.add_selection_below(&Default::default(), cx);
 4418    });
 4419
 4420    cx.assert_editor_state(indoc!(
 4421        r#"abc
 4422           def«ˇg»hi
 4423
 4424           jk
 4425           nlm«ˇo»
 4426           "#
 4427    ));
 4428
 4429    cx.update_editor(|view, cx| {
 4430        view.add_selection_above(&Default::default(), cx);
 4431    });
 4432
 4433    cx.assert_editor_state(indoc!(
 4434        r#"abc
 4435           def«ˇg»hi
 4436
 4437           jk
 4438           nlmo
 4439           "#
 4440    ));
 4441
 4442    cx.update_editor(|view, cx| {
 4443        view.add_selection_above(&Default::default(), cx);
 4444    });
 4445
 4446    cx.assert_editor_state(indoc!(
 4447        r#"abc
 4448           def«ˇg»hi
 4449
 4450           jk
 4451           nlmo
 4452           "#
 4453    ));
 4454
 4455    // Change selections again
 4456    cx.set_state(indoc!(
 4457        r#"a«bc
 4458           defgˇ»hi
 4459
 4460           jk
 4461           nlmo
 4462           "#
 4463    ));
 4464
 4465    cx.update_editor(|view, cx| {
 4466        view.add_selection_below(&Default::default(), cx);
 4467    });
 4468
 4469    cx.assert_editor_state(indoc!(
 4470        r#"a«bcˇ»
 4471           d«efgˇ»hi
 4472
 4473           j«kˇ»
 4474           nlmo
 4475           "#
 4476    ));
 4477
 4478    cx.update_editor(|view, cx| {
 4479        view.add_selection_below(&Default::default(), cx);
 4480    });
 4481    cx.assert_editor_state(indoc!(
 4482        r#"a«bcˇ»
 4483           d«efgˇ»hi
 4484
 4485           j«kˇ»
 4486           n«lmoˇ»
 4487           "#
 4488    ));
 4489    cx.update_editor(|view, cx| {
 4490        view.add_selection_above(&Default::default(), cx);
 4491    });
 4492
 4493    cx.assert_editor_state(indoc!(
 4494        r#"a«bcˇ»
 4495           d«efgˇ»hi
 4496
 4497           j«kˇ»
 4498           nlmo
 4499           "#
 4500    ));
 4501
 4502    // Change selections again
 4503    cx.set_state(indoc!(
 4504        r#"abc
 4505           d«ˇefghi
 4506
 4507           jk
 4508           nlm»o
 4509           "#
 4510    ));
 4511
 4512    cx.update_editor(|view, cx| {
 4513        view.add_selection_above(&Default::default(), cx);
 4514    });
 4515
 4516    cx.assert_editor_state(indoc!(
 4517        r#"a«ˇbc»
 4518           d«ˇef»ghi
 4519
 4520           j«ˇk»
 4521           n«ˇlm»o
 4522           "#
 4523    ));
 4524
 4525    cx.update_editor(|view, cx| {
 4526        view.add_selection_below(&Default::default(), cx);
 4527    });
 4528
 4529    cx.assert_editor_state(indoc!(
 4530        r#"abc
 4531           d«ˇef»ghi
 4532
 4533           j«ˇk»
 4534           n«ˇlm»o
 4535           "#
 4536    ));
 4537}
 4538
 4539#[gpui::test]
 4540async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4541    init_test(cx, |_| {});
 4542
 4543    let mut cx = EditorTestContext::new(cx).await;
 4544    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4545
 4546    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4547        .unwrap();
 4548    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4549
 4550    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4551        .unwrap();
 4552    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4553
 4554    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4555    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4556
 4557    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4558    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4559
 4560    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4561        .unwrap();
 4562    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4563
 4564    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4565        .unwrap();
 4566    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4567}
 4568
 4569#[gpui::test]
 4570async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4571    init_test(cx, |_| {});
 4572
 4573    let mut cx = EditorTestContext::new(cx).await;
 4574    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4575
 4576    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4577        .unwrap();
 4578    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4579}
 4580
 4581#[gpui::test]
 4582async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4583    init_test(cx, |_| {});
 4584
 4585    let mut cx = EditorTestContext::new(cx).await;
 4586    cx.set_state(
 4587        r#"let foo = 2;
 4588lˇet foo = 2;
 4589let fooˇ = 2;
 4590let foo = 2;
 4591let foo = ˇ2;"#,
 4592    );
 4593
 4594    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4595        .unwrap();
 4596    cx.assert_editor_state(
 4597        r#"let foo = 2;
 4598«letˇ» foo = 2;
 4599let «fooˇ» = 2;
 4600let foo = 2;
 4601let foo = «2ˇ»;"#,
 4602    );
 4603
 4604    // noop for multiple selections with different contents
 4605    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4606        .unwrap();
 4607    cx.assert_editor_state(
 4608        r#"let foo = 2;
 4609«letˇ» foo = 2;
 4610let «fooˇ» = 2;
 4611let foo = 2;
 4612let foo = «2ˇ»;"#,
 4613    );
 4614}
 4615
 4616#[gpui::test]
 4617async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4618    init_test(cx, |_| {});
 4619
 4620    let mut cx = EditorTestContext::new_multibuffer(
 4621        cx,
 4622        [
 4623            &indoc! {
 4624                "aaa\n«bbb\nccc\n»ddd"
 4625            },
 4626            &indoc! {
 4627                "aaa\n«bbb\nccc\n»ddd"
 4628            },
 4629        ],
 4630    );
 4631
 4632    cx.assert_editor_state(indoc! {"
 4633        ˇbbb
 4634        ccc
 4635
 4636        bbb
 4637        ccc
 4638        "});
 4639    cx.dispatch_action(SelectPrevious::default());
 4640    cx.assert_editor_state(indoc! {"
 4641                «bbbˇ»
 4642                ccc
 4643
 4644                bbb
 4645                ccc
 4646                "});
 4647    cx.dispatch_action(SelectPrevious::default());
 4648    cx.assert_editor_state(indoc! {"
 4649                «bbbˇ»
 4650                ccc
 4651
 4652                «bbbˇ»
 4653                ccc
 4654                "});
 4655}
 4656
 4657#[gpui::test]
 4658async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4659    init_test(cx, |_| {});
 4660
 4661    let mut cx = EditorTestContext::new(cx).await;
 4662    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4663
 4664    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4665        .unwrap();
 4666    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4667
 4668    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4669        .unwrap();
 4670    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4671
 4672    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4673    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4674
 4675    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4676    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4677
 4678    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4679        .unwrap();
 4680    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4681
 4682    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4683        .unwrap();
 4684    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4685
 4686    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4687        .unwrap();
 4688    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4689}
 4690
 4691#[gpui::test]
 4692async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4693    init_test(cx, |_| {});
 4694
 4695    let mut cx = EditorTestContext::new(cx).await;
 4696    cx.set_state(
 4697        r#"let foo = 2;
 4698lˇet foo = 2;
 4699let fooˇ = 2;
 4700let foo = 2;
 4701let foo = ˇ2;"#,
 4702    );
 4703
 4704    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4705        .unwrap();
 4706    cx.assert_editor_state(
 4707        r#"let foo = 2;
 4708«letˇ» foo = 2;
 4709let «fooˇ» = 2;
 4710let foo = 2;
 4711let foo = «2ˇ»;"#,
 4712    );
 4713
 4714    // noop for multiple selections with different contents
 4715    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4716        .unwrap();
 4717    cx.assert_editor_state(
 4718        r#"let foo = 2;
 4719«letˇ» foo = 2;
 4720let «fooˇ» = 2;
 4721let foo = 2;
 4722let foo = «2ˇ»;"#,
 4723    );
 4724}
 4725
 4726#[gpui::test]
 4727async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4728    init_test(cx, |_| {});
 4729
 4730    let mut cx = EditorTestContext::new(cx).await;
 4731    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4732
 4733    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4734        .unwrap();
 4735    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4736
 4737    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4738        .unwrap();
 4739    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4740
 4741    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4742    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4743
 4744    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4745    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4746
 4747    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4748        .unwrap();
 4749    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4750
 4751    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4752        .unwrap();
 4753    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4754}
 4755
 4756#[gpui::test]
 4757async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4758    init_test(cx, |_| {});
 4759
 4760    let language = Arc::new(Language::new(
 4761        LanguageConfig::default(),
 4762        Some(tree_sitter_rust::language()),
 4763    ));
 4764
 4765    let text = r#"
 4766        use mod1::mod2::{mod3, mod4};
 4767
 4768        fn fn_1(param1: bool, param2: &str) {
 4769            let var1 = "text";
 4770        }
 4771    "#
 4772    .unindent();
 4773
 4774    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4775    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4776    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4777
 4778    editor
 4779        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4780        .await;
 4781
 4782    editor.update(cx, |view, cx| {
 4783        view.change_selections(None, cx, |s| {
 4784            s.select_display_ranges([
 4785                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4786                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4787                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4788            ]);
 4789        });
 4790        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4791    });
 4792    editor.update(cx, |editor, cx| {
 4793        assert_text_with_selections(
 4794            editor,
 4795            indoc! {r#"
 4796                use mod1::mod2::{mod3, «mod4ˇ»};
 4797
 4798                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4799                    let var1 = "«textˇ»";
 4800                }
 4801            "#},
 4802            cx,
 4803        );
 4804    });
 4805
 4806    editor.update(cx, |view, cx| {
 4807        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4808    });
 4809    editor.update(cx, |editor, cx| {
 4810        assert_text_with_selections(
 4811            editor,
 4812            indoc! {r#"
 4813                use mod1::mod2::«{mod3, mod4}ˇ»;
 4814
 4815                «ˇfn fn_1(param1: bool, param2: &str) {
 4816                    let var1 = "text";
 4817 4818            "#},
 4819            cx,
 4820        );
 4821    });
 4822
 4823    editor.update(cx, |view, cx| {
 4824        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4825    });
 4826    assert_eq!(
 4827        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4828        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4829    );
 4830
 4831    // Trying to expand the selected syntax node one more time has no effect.
 4832    editor.update(cx, |view, cx| {
 4833        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4834    });
 4835    assert_eq!(
 4836        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4837        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4838    );
 4839
 4840    editor.update(cx, |view, cx| {
 4841        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4842    });
 4843    editor.update(cx, |editor, cx| {
 4844        assert_text_with_selections(
 4845            editor,
 4846            indoc! {r#"
 4847                use mod1::mod2::«{mod3, mod4}ˇ»;
 4848
 4849                «ˇfn fn_1(param1: bool, param2: &str) {
 4850                    let var1 = "text";
 4851 4852            "#},
 4853            cx,
 4854        );
 4855    });
 4856
 4857    editor.update(cx, |view, cx| {
 4858        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4859    });
 4860    editor.update(cx, |editor, cx| {
 4861        assert_text_with_selections(
 4862            editor,
 4863            indoc! {r#"
 4864                use mod1::mod2::{mod3, «mod4ˇ»};
 4865
 4866                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4867                    let var1 = "«textˇ»";
 4868                }
 4869            "#},
 4870            cx,
 4871        );
 4872    });
 4873
 4874    editor.update(cx, |view, cx| {
 4875        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4876    });
 4877    editor.update(cx, |editor, cx| {
 4878        assert_text_with_selections(
 4879            editor,
 4880            indoc! {r#"
 4881                use mod1::mod2::{mod3, mo«ˇ»d4};
 4882
 4883                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4884                    let var1 = "te«ˇ»xt";
 4885                }
 4886            "#},
 4887            cx,
 4888        );
 4889    });
 4890
 4891    // Trying to shrink the selected syntax node one more time has no effect.
 4892    editor.update(cx, |view, cx| {
 4893        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4894    });
 4895    editor.update(cx, |editor, cx| {
 4896        assert_text_with_selections(
 4897            editor,
 4898            indoc! {r#"
 4899                use mod1::mod2::{mod3, mo«ˇ»d4};
 4900
 4901                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4902                    let var1 = "te«ˇ»xt";
 4903                }
 4904            "#},
 4905            cx,
 4906        );
 4907    });
 4908
 4909    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4910    // a fold.
 4911    editor.update(cx, |view, cx| {
 4912        view.fold_ranges(
 4913            vec![
 4914                (
 4915                    Point::new(0, 21)..Point::new(0, 24),
 4916                    FoldPlaceholder::test(),
 4917                ),
 4918                (
 4919                    Point::new(3, 20)..Point::new(3, 22),
 4920                    FoldPlaceholder::test(),
 4921                ),
 4922            ],
 4923            true,
 4924            cx,
 4925        );
 4926        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4927    });
 4928    editor.update(cx, |editor, cx| {
 4929        assert_text_with_selections(
 4930            editor,
 4931            indoc! {r#"
 4932                use mod1::mod2::«{mod3, mod4}ˇ»;
 4933
 4934                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4935                    «let var1 = "text";ˇ»
 4936                }
 4937            "#},
 4938            cx,
 4939        );
 4940    });
 4941}
 4942
 4943#[gpui::test]
 4944async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4945    init_test(cx, |_| {});
 4946
 4947    let language = Arc::new(
 4948        Language::new(
 4949            LanguageConfig {
 4950                brackets: BracketPairConfig {
 4951                    pairs: vec![
 4952                        BracketPair {
 4953                            start: "{".to_string(),
 4954                            end: "}".to_string(),
 4955                            close: false,
 4956                            surround: false,
 4957                            newline: true,
 4958                        },
 4959                        BracketPair {
 4960                            start: "(".to_string(),
 4961                            end: ")".to_string(),
 4962                            close: false,
 4963                            surround: false,
 4964                            newline: true,
 4965                        },
 4966                    ],
 4967                    ..Default::default()
 4968                },
 4969                ..Default::default()
 4970            },
 4971            Some(tree_sitter_rust::language()),
 4972        )
 4973        .with_indents_query(
 4974            r#"
 4975                (_ "(" ")" @end) @indent
 4976                (_ "{" "}" @end) @indent
 4977            "#,
 4978        )
 4979        .unwrap(),
 4980    );
 4981
 4982    let text = "fn a() {}";
 4983
 4984    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4985    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4986    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4987    editor
 4988        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4989        .await;
 4990
 4991    _ = editor.update(cx, |editor, cx| {
 4992        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4993        editor.newline(&Newline, cx);
 4994        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4995        assert_eq!(
 4996            editor.selections.ranges(cx),
 4997            &[
 4998                Point::new(1, 4)..Point::new(1, 4),
 4999                Point::new(3, 4)..Point::new(3, 4),
 5000                Point::new(5, 0)..Point::new(5, 0)
 5001            ]
 5002        );
 5003    });
 5004}
 5005
 5006#[gpui::test]
 5007async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5008    init_test(cx, |_| {});
 5009
 5010    let mut cx = EditorTestContext::new(cx).await;
 5011
 5012    let language = Arc::new(Language::new(
 5013        LanguageConfig {
 5014            brackets: BracketPairConfig {
 5015                pairs: vec![
 5016                    BracketPair {
 5017                        start: "{".to_string(),
 5018                        end: "}".to_string(),
 5019                        close: true,
 5020                        surround: true,
 5021                        newline: true,
 5022                    },
 5023                    BracketPair {
 5024                        start: "(".to_string(),
 5025                        end: ")".to_string(),
 5026                        close: true,
 5027                        surround: true,
 5028                        newline: true,
 5029                    },
 5030                    BracketPair {
 5031                        start: "/*".to_string(),
 5032                        end: " */".to_string(),
 5033                        close: true,
 5034                        surround: true,
 5035                        newline: true,
 5036                    },
 5037                    BracketPair {
 5038                        start: "[".to_string(),
 5039                        end: "]".to_string(),
 5040                        close: false,
 5041                        surround: false,
 5042                        newline: true,
 5043                    },
 5044                    BracketPair {
 5045                        start: "\"".to_string(),
 5046                        end: "\"".to_string(),
 5047                        close: true,
 5048                        surround: true,
 5049                        newline: false,
 5050                    },
 5051                    BracketPair {
 5052                        start: "<".to_string(),
 5053                        end: ">".to_string(),
 5054                        close: false,
 5055                        surround: true,
 5056                        newline: true,
 5057                    },
 5058                ],
 5059                ..Default::default()
 5060            },
 5061            autoclose_before: "})]".to_string(),
 5062            ..Default::default()
 5063        },
 5064        Some(tree_sitter_rust::language()),
 5065    ));
 5066
 5067    cx.language_registry().add(language.clone());
 5068    cx.update_buffer(|buffer, cx| {
 5069        buffer.set_language(Some(language), cx);
 5070    });
 5071
 5072    cx.set_state(
 5073        &r#"
 5074            🏀ˇ
 5075            εˇ
 5076            ❤️ˇ
 5077        "#
 5078        .unindent(),
 5079    );
 5080
 5081    // autoclose multiple nested brackets at multiple cursors
 5082    cx.update_editor(|view, cx| {
 5083        view.handle_input("{", cx);
 5084        view.handle_input("{", cx);
 5085        view.handle_input("{", cx);
 5086    });
 5087    cx.assert_editor_state(
 5088        &"
 5089            🏀{{{ˇ}}}
 5090            ε{{{ˇ}}}
 5091            ❤️{{{ˇ}}}
 5092        "
 5093        .unindent(),
 5094    );
 5095
 5096    // insert a different closing bracket
 5097    cx.update_editor(|view, cx| {
 5098        view.handle_input(")", cx);
 5099    });
 5100    cx.assert_editor_state(
 5101        &"
 5102            🏀{{{)ˇ}}}
 5103            ε{{{)ˇ}}}
 5104            ❤️{{{)ˇ}}}
 5105        "
 5106        .unindent(),
 5107    );
 5108
 5109    // skip over the auto-closed brackets when typing a closing bracket
 5110    cx.update_editor(|view, cx| {
 5111        view.move_right(&MoveRight, cx);
 5112        view.handle_input("}", cx);
 5113        view.handle_input("}", cx);
 5114        view.handle_input("}", cx);
 5115    });
 5116    cx.assert_editor_state(
 5117        &"
 5118            🏀{{{)}}}}ˇ
 5119            ε{{{)}}}}ˇ
 5120            ❤️{{{)}}}}ˇ
 5121        "
 5122        .unindent(),
 5123    );
 5124
 5125    // autoclose multi-character pairs
 5126    cx.set_state(
 5127        &"
 5128            ˇ
 5129            ˇ
 5130        "
 5131        .unindent(),
 5132    );
 5133    cx.update_editor(|view, cx| {
 5134        view.handle_input("/", cx);
 5135        view.handle_input("*", cx);
 5136    });
 5137    cx.assert_editor_state(
 5138        &"
 5139            /*ˇ */
 5140            /*ˇ */
 5141        "
 5142        .unindent(),
 5143    );
 5144
 5145    // one cursor autocloses a multi-character pair, one cursor
 5146    // does not autoclose.
 5147    cx.set_state(
 5148        &"
 5149 5150            ˇ
 5151        "
 5152        .unindent(),
 5153    );
 5154    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5155    cx.assert_editor_state(
 5156        &"
 5157            /*ˇ */
 5158 5159        "
 5160        .unindent(),
 5161    );
 5162
 5163    // Don't autoclose if the next character isn't whitespace and isn't
 5164    // listed in the language's "autoclose_before" section.
 5165    cx.set_state("ˇa b");
 5166    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5167    cx.assert_editor_state("{ˇa b");
 5168
 5169    // Don't autoclose if `close` is false for the bracket pair
 5170    cx.set_state("ˇ");
 5171    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5172    cx.assert_editor_state("");
 5173
 5174    // Surround with brackets if text is selected
 5175    cx.set_state("«aˇ» b");
 5176    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5177    cx.assert_editor_state("{«aˇ»} b");
 5178
 5179    // Autclose pair where the start and end characters are the same
 5180    cx.set_state("");
 5181    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5182    cx.assert_editor_state("a\"ˇ\"");
 5183    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5184    cx.assert_editor_state("a\"\"ˇ");
 5185
 5186    // Don't autoclose pair if autoclose is disabled
 5187    cx.set_state("ˇ");
 5188    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5189    cx.assert_editor_state("");
 5190
 5191    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5192    cx.set_state("«aˇ» b");
 5193    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5194    cx.assert_editor_state("<«aˇ»> b");
 5195}
 5196
 5197#[gpui::test]
 5198async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5199    init_test(cx, |settings| {
 5200        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5201    });
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204
 5205    let language = Arc::new(Language::new(
 5206        LanguageConfig {
 5207            brackets: BracketPairConfig {
 5208                pairs: vec![
 5209                    BracketPair {
 5210                        start: "{".to_string(),
 5211                        end: "}".to_string(),
 5212                        close: true,
 5213                        surround: true,
 5214                        newline: true,
 5215                    },
 5216                    BracketPair {
 5217                        start: "(".to_string(),
 5218                        end: ")".to_string(),
 5219                        close: true,
 5220                        surround: true,
 5221                        newline: true,
 5222                    },
 5223                    BracketPair {
 5224                        start: "[".to_string(),
 5225                        end: "]".to_string(),
 5226                        close: false,
 5227                        surround: false,
 5228                        newline: true,
 5229                    },
 5230                ],
 5231                ..Default::default()
 5232            },
 5233            autoclose_before: "})]".to_string(),
 5234            ..Default::default()
 5235        },
 5236        Some(tree_sitter_rust::language()),
 5237    ));
 5238
 5239    cx.language_registry().add(language.clone());
 5240    cx.update_buffer(|buffer, cx| {
 5241        buffer.set_language(Some(language), cx);
 5242    });
 5243
 5244    cx.set_state(
 5245        &"
 5246            ˇ
 5247            ˇ
 5248            ˇ
 5249        "
 5250        .unindent(),
 5251    );
 5252
 5253    // ensure only matching closing brackets are skipped over
 5254    cx.update_editor(|view, cx| {
 5255        view.handle_input("}", cx);
 5256        view.move_left(&MoveLeft, cx);
 5257        view.handle_input(")", cx);
 5258        view.move_left(&MoveLeft, cx);
 5259    });
 5260    cx.assert_editor_state(
 5261        &"
 5262            ˇ)}
 5263            ˇ)}
 5264            ˇ)}
 5265        "
 5266        .unindent(),
 5267    );
 5268
 5269    // skip-over closing brackets at multiple cursors
 5270    cx.update_editor(|view, cx| {
 5271        view.handle_input(")", cx);
 5272        view.handle_input("}", cx);
 5273    });
 5274    cx.assert_editor_state(
 5275        &"
 5276            )}ˇ
 5277            )}ˇ
 5278            )}ˇ
 5279        "
 5280        .unindent(),
 5281    );
 5282
 5283    // ignore non-close brackets
 5284    cx.update_editor(|view, cx| {
 5285        view.handle_input("]", cx);
 5286        view.move_left(&MoveLeft, cx);
 5287        view.handle_input("]", cx);
 5288    });
 5289    cx.assert_editor_state(
 5290        &"
 5291            )}]ˇ]
 5292            )}]ˇ]
 5293            )}]ˇ]
 5294        "
 5295        .unindent(),
 5296    );
 5297}
 5298
 5299#[gpui::test]
 5300async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5301    init_test(cx, |_| {});
 5302
 5303    let mut cx = EditorTestContext::new(cx).await;
 5304
 5305    let html_language = Arc::new(
 5306        Language::new(
 5307            LanguageConfig {
 5308                name: "HTML".into(),
 5309                brackets: BracketPairConfig {
 5310                    pairs: vec![
 5311                        BracketPair {
 5312                            start: "<".into(),
 5313                            end: ">".into(),
 5314                            close: true,
 5315                            ..Default::default()
 5316                        },
 5317                        BracketPair {
 5318                            start: "{".into(),
 5319                            end: "}".into(),
 5320                            close: true,
 5321                            ..Default::default()
 5322                        },
 5323                        BracketPair {
 5324                            start: "(".into(),
 5325                            end: ")".into(),
 5326                            close: true,
 5327                            ..Default::default()
 5328                        },
 5329                    ],
 5330                    ..Default::default()
 5331                },
 5332                autoclose_before: "})]>".into(),
 5333                ..Default::default()
 5334            },
 5335            Some(tree_sitter_html::language()),
 5336        )
 5337        .with_injection_query(
 5338            r#"
 5339            (script_element
 5340                (raw_text) @content
 5341                (#set! "language" "javascript"))
 5342            "#,
 5343        )
 5344        .unwrap(),
 5345    );
 5346
 5347    let javascript_language = Arc::new(Language::new(
 5348        LanguageConfig {
 5349            name: "JavaScript".into(),
 5350            brackets: BracketPairConfig {
 5351                pairs: vec![
 5352                    BracketPair {
 5353                        start: "/*".into(),
 5354                        end: " */".into(),
 5355                        close: true,
 5356                        ..Default::default()
 5357                    },
 5358                    BracketPair {
 5359                        start: "{".into(),
 5360                        end: "}".into(),
 5361                        close: true,
 5362                        ..Default::default()
 5363                    },
 5364                    BracketPair {
 5365                        start: "(".into(),
 5366                        end: ")".into(),
 5367                        close: true,
 5368                        ..Default::default()
 5369                    },
 5370                ],
 5371                ..Default::default()
 5372            },
 5373            autoclose_before: "})]>".into(),
 5374            ..Default::default()
 5375        },
 5376        Some(tree_sitter_typescript::language_tsx()),
 5377    ));
 5378
 5379    cx.language_registry().add(html_language.clone());
 5380    cx.language_registry().add(javascript_language.clone());
 5381
 5382    cx.update_buffer(|buffer, cx| {
 5383        buffer.set_language(Some(html_language), cx);
 5384    });
 5385
 5386    cx.set_state(
 5387        &r#"
 5388            <body>ˇ
 5389                <script>
 5390                    var x = 1;ˇ
 5391                </script>
 5392            </body>ˇ
 5393        "#
 5394        .unindent(),
 5395    );
 5396
 5397    // Precondition: different languages are active at different locations.
 5398    cx.update_editor(|editor, cx| {
 5399        let snapshot = editor.snapshot(cx);
 5400        let cursors = editor.selections.ranges::<usize>(cx);
 5401        let languages = cursors
 5402            .iter()
 5403            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5404            .collect::<Vec<_>>();
 5405        assert_eq!(
 5406            languages,
 5407            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5408        );
 5409    });
 5410
 5411    // Angle brackets autoclose in HTML, but not JavaScript.
 5412    cx.update_editor(|editor, cx| {
 5413        editor.handle_input("<", cx);
 5414        editor.handle_input("a", cx);
 5415    });
 5416    cx.assert_editor_state(
 5417        &r#"
 5418            <body><aˇ>
 5419                <script>
 5420                    var x = 1;<aˇ
 5421                </script>
 5422            </body><aˇ>
 5423        "#
 5424        .unindent(),
 5425    );
 5426
 5427    // Curly braces and parens autoclose in both HTML and JavaScript.
 5428    cx.update_editor(|editor, cx| {
 5429        editor.handle_input(" b=", cx);
 5430        editor.handle_input("{", cx);
 5431        editor.handle_input("c", cx);
 5432        editor.handle_input("(", cx);
 5433    });
 5434    cx.assert_editor_state(
 5435        &r#"
 5436            <body><a b={c(ˇ)}>
 5437                <script>
 5438                    var x = 1;<a b={c(ˇ)}
 5439                </script>
 5440            </body><a b={c(ˇ)}>
 5441        "#
 5442        .unindent(),
 5443    );
 5444
 5445    // Brackets that were already autoclosed are skipped.
 5446    cx.update_editor(|editor, cx| {
 5447        editor.handle_input(")", cx);
 5448        editor.handle_input("d", cx);
 5449        editor.handle_input("}", cx);
 5450    });
 5451    cx.assert_editor_state(
 5452        &r#"
 5453            <body><a b={c()d}ˇ>
 5454                <script>
 5455                    var x = 1;<a b={c()d}ˇ
 5456                </script>
 5457            </body><a b={c()d}ˇ>
 5458        "#
 5459        .unindent(),
 5460    );
 5461    cx.update_editor(|editor, cx| {
 5462        editor.handle_input(">", cx);
 5463    });
 5464    cx.assert_editor_state(
 5465        &r#"
 5466            <body><a b={c()d}>ˇ
 5467                <script>
 5468                    var x = 1;<a b={c()d}>ˇ
 5469                </script>
 5470            </body><a b={c()d}>ˇ
 5471        "#
 5472        .unindent(),
 5473    );
 5474
 5475    // Reset
 5476    cx.set_state(
 5477        &r#"
 5478            <body>ˇ
 5479                <script>
 5480                    var x = 1;ˇ
 5481                </script>
 5482            </body>ˇ
 5483        "#
 5484        .unindent(),
 5485    );
 5486
 5487    cx.update_editor(|editor, cx| {
 5488        editor.handle_input("<", cx);
 5489    });
 5490    cx.assert_editor_state(
 5491        &r#"
 5492            <body><ˇ>
 5493                <script>
 5494                    var x = 1;<ˇ
 5495                </script>
 5496            </body><ˇ>
 5497        "#
 5498        .unindent(),
 5499    );
 5500
 5501    // When backspacing, the closing angle brackets are removed.
 5502    cx.update_editor(|editor, cx| {
 5503        editor.backspace(&Backspace, cx);
 5504    });
 5505    cx.assert_editor_state(
 5506        &r#"
 5507            <body>ˇ
 5508                <script>
 5509                    var x = 1;ˇ
 5510                </script>
 5511            </body>ˇ
 5512        "#
 5513        .unindent(),
 5514    );
 5515
 5516    // Block comments autoclose in JavaScript, but not HTML.
 5517    cx.update_editor(|editor, cx| {
 5518        editor.handle_input("/", cx);
 5519        editor.handle_input("*", cx);
 5520    });
 5521    cx.assert_editor_state(
 5522        &r#"
 5523            <body>/*ˇ
 5524                <script>
 5525                    var x = 1;/*ˇ */
 5526                </script>
 5527            </body>/*ˇ
 5528        "#
 5529        .unindent(),
 5530    );
 5531}
 5532
 5533#[gpui::test]
 5534async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5535    init_test(cx, |_| {});
 5536
 5537    let mut cx = EditorTestContext::new(cx).await;
 5538
 5539    let rust_language = Arc::new(
 5540        Language::new(
 5541            LanguageConfig {
 5542                name: "Rust".into(),
 5543                brackets: serde_json::from_value(json!([
 5544                    { "start": "{", "end": "}", "close": true, "newline": true },
 5545                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5546                ]))
 5547                .unwrap(),
 5548                autoclose_before: "})]>".into(),
 5549                ..Default::default()
 5550            },
 5551            Some(tree_sitter_rust::language()),
 5552        )
 5553        .with_override_query("(string_literal) @string")
 5554        .unwrap(),
 5555    );
 5556
 5557    cx.language_registry().add(rust_language.clone());
 5558    cx.update_buffer(|buffer, cx| {
 5559        buffer.set_language(Some(rust_language), cx);
 5560    });
 5561
 5562    cx.set_state(
 5563        &r#"
 5564            let x = ˇ
 5565        "#
 5566        .unindent(),
 5567    );
 5568
 5569    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5570    cx.update_editor(|editor, cx| {
 5571        editor.handle_input("\"", cx);
 5572    });
 5573    cx.assert_editor_state(
 5574        &r#"
 5575            let x = "ˇ"
 5576        "#
 5577        .unindent(),
 5578    );
 5579
 5580    // Inserting another quotation mark. The cursor moves across the existing
 5581    // automatically-inserted quotation mark.
 5582    cx.update_editor(|editor, cx| {
 5583        editor.handle_input("\"", cx);
 5584    });
 5585    cx.assert_editor_state(
 5586        &r#"
 5587            let x = ""ˇ
 5588        "#
 5589        .unindent(),
 5590    );
 5591
 5592    // Reset
 5593    cx.set_state(
 5594        &r#"
 5595            let x = ˇ
 5596        "#
 5597        .unindent(),
 5598    );
 5599
 5600    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5601    cx.update_editor(|editor, cx| {
 5602        editor.handle_input("\"", cx);
 5603        editor.handle_input(" ", cx);
 5604        editor.move_left(&Default::default(), cx);
 5605        editor.handle_input("\\", cx);
 5606        editor.handle_input("\"", cx);
 5607    });
 5608    cx.assert_editor_state(
 5609        &r#"
 5610            let x = "\"ˇ "
 5611        "#
 5612        .unindent(),
 5613    );
 5614
 5615    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5616    // mark. Nothing is inserted.
 5617    cx.update_editor(|editor, cx| {
 5618        editor.move_right(&Default::default(), cx);
 5619        editor.handle_input("\"", cx);
 5620    });
 5621    cx.assert_editor_state(
 5622        &r#"
 5623            let x = "\" "ˇ
 5624        "#
 5625        .unindent(),
 5626    );
 5627}
 5628
 5629#[gpui::test]
 5630async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5631    init_test(cx, |_| {});
 5632
 5633    let language = Arc::new(Language::new(
 5634        LanguageConfig {
 5635            brackets: BracketPairConfig {
 5636                pairs: vec![
 5637                    BracketPair {
 5638                        start: "{".to_string(),
 5639                        end: "}".to_string(),
 5640                        close: true,
 5641                        surround: true,
 5642                        newline: true,
 5643                    },
 5644                    BracketPair {
 5645                        start: "/* ".to_string(),
 5646                        end: "*/".to_string(),
 5647                        close: true,
 5648                        surround: true,
 5649                        ..Default::default()
 5650                    },
 5651                ],
 5652                ..Default::default()
 5653            },
 5654            ..Default::default()
 5655        },
 5656        Some(tree_sitter_rust::language()),
 5657    ));
 5658
 5659    let text = r#"
 5660        a
 5661        b
 5662        c
 5663    "#
 5664    .unindent();
 5665
 5666    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5667    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5668    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5669    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5670        .await;
 5671
 5672    _ = view.update(cx, |view, cx| {
 5673        view.change_selections(None, cx, |s| {
 5674            s.select_display_ranges([
 5675                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5676                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5677                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5678            ])
 5679        });
 5680
 5681        view.handle_input("{", cx);
 5682        view.handle_input("{", cx);
 5683        view.handle_input("{", cx);
 5684        assert_eq!(
 5685            view.text(cx),
 5686            "
 5687                {{{a}}}
 5688                {{{b}}}
 5689                {{{c}}}
 5690            "
 5691            .unindent()
 5692        );
 5693        assert_eq!(
 5694            view.selections.display_ranges(cx),
 5695            [
 5696                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5697                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5698                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5699            ]
 5700        );
 5701
 5702        view.undo(&Undo, cx);
 5703        view.undo(&Undo, cx);
 5704        view.undo(&Undo, cx);
 5705        assert_eq!(
 5706            view.text(cx),
 5707            "
 5708                a
 5709                b
 5710                c
 5711            "
 5712            .unindent()
 5713        );
 5714        assert_eq!(
 5715            view.selections.display_ranges(cx),
 5716            [
 5717                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5718                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5719                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5720            ]
 5721        );
 5722
 5723        // Ensure inserting the first character of a multi-byte bracket pair
 5724        // doesn't surround the selections with the bracket.
 5725        view.handle_input("/", cx);
 5726        assert_eq!(
 5727            view.text(cx),
 5728            "
 5729                /
 5730                /
 5731                /
 5732            "
 5733            .unindent()
 5734        );
 5735        assert_eq!(
 5736            view.selections.display_ranges(cx),
 5737            [
 5738                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5739                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5740                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5741            ]
 5742        );
 5743
 5744        view.undo(&Undo, cx);
 5745        assert_eq!(
 5746            view.text(cx),
 5747            "
 5748                a
 5749                b
 5750                c
 5751            "
 5752            .unindent()
 5753        );
 5754        assert_eq!(
 5755            view.selections.display_ranges(cx),
 5756            [
 5757                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5758                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5759                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5760            ]
 5761        );
 5762
 5763        // Ensure inserting the last character of a multi-byte bracket pair
 5764        // doesn't surround the selections with the bracket.
 5765        view.handle_input("*", cx);
 5766        assert_eq!(
 5767            view.text(cx),
 5768            "
 5769                *
 5770                *
 5771                *
 5772            "
 5773            .unindent()
 5774        );
 5775        assert_eq!(
 5776            view.selections.display_ranges(cx),
 5777            [
 5778                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5779                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5780                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5781            ]
 5782        );
 5783    });
 5784}
 5785
 5786#[gpui::test]
 5787async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5788    init_test(cx, |_| {});
 5789
 5790    let language = Arc::new(Language::new(
 5791        LanguageConfig {
 5792            brackets: BracketPairConfig {
 5793                pairs: vec![BracketPair {
 5794                    start: "{".to_string(),
 5795                    end: "}".to_string(),
 5796                    close: true,
 5797                    surround: true,
 5798                    newline: true,
 5799                }],
 5800                ..Default::default()
 5801            },
 5802            autoclose_before: "}".to_string(),
 5803            ..Default::default()
 5804        },
 5805        Some(tree_sitter_rust::language()),
 5806    ));
 5807
 5808    let text = r#"
 5809        a
 5810        b
 5811        c
 5812    "#
 5813    .unindent();
 5814
 5815    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5816    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5817    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5818    editor
 5819        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5820        .await;
 5821
 5822    _ = editor.update(cx, |editor, cx| {
 5823        editor.change_selections(None, cx, |s| {
 5824            s.select_ranges([
 5825                Point::new(0, 1)..Point::new(0, 1),
 5826                Point::new(1, 1)..Point::new(1, 1),
 5827                Point::new(2, 1)..Point::new(2, 1),
 5828            ])
 5829        });
 5830
 5831        editor.handle_input("{", cx);
 5832        editor.handle_input("{", cx);
 5833        editor.handle_input("_", cx);
 5834        assert_eq!(
 5835            editor.text(cx),
 5836            "
 5837                a{{_}}
 5838                b{{_}}
 5839                c{{_}}
 5840            "
 5841            .unindent()
 5842        );
 5843        assert_eq!(
 5844            editor.selections.ranges::<Point>(cx),
 5845            [
 5846                Point::new(0, 4)..Point::new(0, 4),
 5847                Point::new(1, 4)..Point::new(1, 4),
 5848                Point::new(2, 4)..Point::new(2, 4)
 5849            ]
 5850        );
 5851
 5852        editor.backspace(&Default::default(), cx);
 5853        editor.backspace(&Default::default(), cx);
 5854        assert_eq!(
 5855            editor.text(cx),
 5856            "
 5857                a{}
 5858                b{}
 5859                c{}
 5860            "
 5861            .unindent()
 5862        );
 5863        assert_eq!(
 5864            editor.selections.ranges::<Point>(cx),
 5865            [
 5866                Point::new(0, 2)..Point::new(0, 2),
 5867                Point::new(1, 2)..Point::new(1, 2),
 5868                Point::new(2, 2)..Point::new(2, 2)
 5869            ]
 5870        );
 5871
 5872        editor.delete_to_previous_word_start(&Default::default(), cx);
 5873        assert_eq!(
 5874            editor.text(cx),
 5875            "
 5876                a
 5877                b
 5878                c
 5879            "
 5880            .unindent()
 5881        );
 5882        assert_eq!(
 5883            editor.selections.ranges::<Point>(cx),
 5884            [
 5885                Point::new(0, 1)..Point::new(0, 1),
 5886                Point::new(1, 1)..Point::new(1, 1),
 5887                Point::new(2, 1)..Point::new(2, 1)
 5888            ]
 5889        );
 5890    });
 5891}
 5892
 5893#[gpui::test]
 5894async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5895    init_test(cx, |settings| {
 5896        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5897    });
 5898
 5899    let mut cx = EditorTestContext::new(cx).await;
 5900
 5901    let language = Arc::new(Language::new(
 5902        LanguageConfig {
 5903            brackets: BracketPairConfig {
 5904                pairs: vec![
 5905                    BracketPair {
 5906                        start: "{".to_string(),
 5907                        end: "}".to_string(),
 5908                        close: true,
 5909                        surround: true,
 5910                        newline: true,
 5911                    },
 5912                    BracketPair {
 5913                        start: "(".to_string(),
 5914                        end: ")".to_string(),
 5915                        close: true,
 5916                        surround: true,
 5917                        newline: true,
 5918                    },
 5919                    BracketPair {
 5920                        start: "[".to_string(),
 5921                        end: "]".to_string(),
 5922                        close: false,
 5923                        surround: true,
 5924                        newline: true,
 5925                    },
 5926                ],
 5927                ..Default::default()
 5928            },
 5929            autoclose_before: "})]".to_string(),
 5930            ..Default::default()
 5931        },
 5932        Some(tree_sitter_rust::language()),
 5933    ));
 5934
 5935    cx.language_registry().add(language.clone());
 5936    cx.update_buffer(|buffer, cx| {
 5937        buffer.set_language(Some(language), cx);
 5938    });
 5939
 5940    cx.set_state(
 5941        &"
 5942            {(ˇ)}
 5943            [[ˇ]]
 5944            {(ˇ)}
 5945        "
 5946        .unindent(),
 5947    );
 5948
 5949    cx.update_editor(|view, cx| {
 5950        view.backspace(&Default::default(), cx);
 5951        view.backspace(&Default::default(), cx);
 5952    });
 5953
 5954    cx.assert_editor_state(
 5955        &"
 5956            ˇ
 5957            ˇ]]
 5958            ˇ
 5959        "
 5960        .unindent(),
 5961    );
 5962
 5963    cx.update_editor(|view, cx| {
 5964        view.handle_input("{", cx);
 5965        view.handle_input("{", cx);
 5966        view.move_right(&MoveRight, cx);
 5967        view.move_right(&MoveRight, cx);
 5968        view.move_left(&MoveLeft, cx);
 5969        view.move_left(&MoveLeft, cx);
 5970        view.backspace(&Default::default(), cx);
 5971    });
 5972
 5973    cx.assert_editor_state(
 5974        &"
 5975            {ˇ}
 5976            {ˇ}]]
 5977            {ˇ}
 5978        "
 5979        .unindent(),
 5980    );
 5981
 5982    cx.update_editor(|view, cx| {
 5983        view.backspace(&Default::default(), cx);
 5984    });
 5985
 5986    cx.assert_editor_state(
 5987        &"
 5988            ˇ
 5989            ˇ]]
 5990            ˇ
 5991        "
 5992        .unindent(),
 5993    );
 5994}
 5995
 5996#[gpui::test]
 5997async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5998    init_test(cx, |_| {});
 5999
 6000    let language = Arc::new(Language::new(
 6001        LanguageConfig::default(),
 6002        Some(tree_sitter_rust::language()),
 6003    ));
 6004
 6005    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6006    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6007    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6008    editor
 6009        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6010        .await;
 6011
 6012    _ = editor.update(cx, |editor, cx| {
 6013        editor.set_auto_replace_emoji_shortcode(true);
 6014
 6015        editor.handle_input("Hello ", cx);
 6016        editor.handle_input(":wave", cx);
 6017        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6018
 6019        editor.handle_input(":", cx);
 6020        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6021
 6022        editor.handle_input(" :smile", cx);
 6023        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6024
 6025        editor.handle_input(":", cx);
 6026        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6027
 6028        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6029        editor.handle_input(":wave", cx);
 6030        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6031
 6032        editor.handle_input(":", cx);
 6033        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6034
 6035        editor.handle_input(":1", cx);
 6036        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6037
 6038        editor.handle_input(":", cx);
 6039        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6040
 6041        // Ensure shortcode does not get replaced when it is part of a word
 6042        editor.handle_input(" Test:wave", cx);
 6043        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6044
 6045        editor.handle_input(":", cx);
 6046        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6047
 6048        editor.set_auto_replace_emoji_shortcode(false);
 6049
 6050        // Ensure shortcode does not get replaced when auto replace is off
 6051        editor.handle_input(" :wave", cx);
 6052        assert_eq!(
 6053            editor.text(cx),
 6054            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6055        );
 6056
 6057        editor.handle_input(":", cx);
 6058        assert_eq!(
 6059            editor.text(cx),
 6060            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6061        );
 6062    });
 6063}
 6064
 6065#[gpui::test]
 6066async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6067    init_test(cx, |_| {});
 6068
 6069    let (text, insertion_ranges) = marked_text_ranges(
 6070        indoc! {"
 6071            a.ˇ b
 6072            a.ˇ b
 6073            a.ˇ b
 6074        "},
 6075        false,
 6076    );
 6077
 6078    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6079    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6080
 6081    _ = editor.update(cx, |editor, cx| {
 6082        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6083
 6084        editor
 6085            .insert_snippet(&insertion_ranges, snippet, cx)
 6086            .unwrap();
 6087
 6088        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6089            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6090            assert_eq!(editor.text(cx), expected_text);
 6091            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6092        }
 6093
 6094        assert(
 6095            editor,
 6096            cx,
 6097            indoc! {"
 6098                a.f(«one», two, «three») b
 6099                a.f(«one», two, «three») b
 6100                a.f(«one», two, «three») b
 6101            "},
 6102        );
 6103
 6104        // Can't move earlier than the first tab stop
 6105        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6106        assert(
 6107            editor,
 6108            cx,
 6109            indoc! {"
 6110                a.f(«one», two, «three») b
 6111                a.f(«one», two, «three») b
 6112                a.f(«one», two, «three») b
 6113            "},
 6114        );
 6115
 6116        assert!(editor.move_to_next_snippet_tabstop(cx));
 6117        assert(
 6118            editor,
 6119            cx,
 6120            indoc! {"
 6121                a.f(one, «two», three) b
 6122                a.f(one, «two», three) b
 6123                a.f(one, «two», three) b
 6124            "},
 6125        );
 6126
 6127        editor.move_to_prev_snippet_tabstop(cx);
 6128        assert(
 6129            editor,
 6130            cx,
 6131            indoc! {"
 6132                a.f(«one», two, «three») b
 6133                a.f(«one», two, «three») b
 6134                a.f(«one», two, «three») b
 6135            "},
 6136        );
 6137
 6138        assert!(editor.move_to_next_snippet_tabstop(cx));
 6139        assert(
 6140            editor,
 6141            cx,
 6142            indoc! {"
 6143                a.f(one, «two», three) b
 6144                a.f(one, «two», three) b
 6145                a.f(one, «two», three) b
 6146            "},
 6147        );
 6148        assert!(editor.move_to_next_snippet_tabstop(cx));
 6149        assert(
 6150            editor,
 6151            cx,
 6152            indoc! {"
 6153                a.f(one, two, three)ˇ b
 6154                a.f(one, two, three)ˇ b
 6155                a.f(one, two, three)ˇ b
 6156            "},
 6157        );
 6158
 6159        // As soon as the last tab stop is reached, snippet state is gone
 6160        editor.move_to_prev_snippet_tabstop(cx);
 6161        assert(
 6162            editor,
 6163            cx,
 6164            indoc! {"
 6165                a.f(one, two, three)ˇ b
 6166                a.f(one, two, three)ˇ b
 6167                a.f(one, two, three)ˇ b
 6168            "},
 6169        );
 6170    });
 6171}
 6172
 6173#[gpui::test]
 6174async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6175    init_test(cx, |_| {});
 6176
 6177    let fs = FakeFs::new(cx.executor());
 6178    fs.insert_file("/file.rs", Default::default()).await;
 6179
 6180    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6181
 6182    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6183    language_registry.add(rust_lang());
 6184    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6185        "Rust",
 6186        FakeLspAdapter {
 6187            capabilities: lsp::ServerCapabilities {
 6188                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6189                ..Default::default()
 6190            },
 6191            ..Default::default()
 6192        },
 6193    );
 6194
 6195    let buffer = project
 6196        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6197        .await
 6198        .unwrap();
 6199
 6200    cx.executor().start_waiting();
 6201    let fake_server = fake_servers.next().await.unwrap();
 6202
 6203    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6204    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6205    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6206    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6207
 6208    let save = editor
 6209        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6210        .unwrap();
 6211    fake_server
 6212        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6213            assert_eq!(
 6214                params.text_document.uri,
 6215                lsp::Url::from_file_path("/file.rs").unwrap()
 6216            );
 6217            assert_eq!(params.options.tab_size, 4);
 6218            Ok(Some(vec![lsp::TextEdit::new(
 6219                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6220                ", ".to_string(),
 6221            )]))
 6222        })
 6223        .next()
 6224        .await;
 6225    cx.executor().start_waiting();
 6226    save.await;
 6227
 6228    assert_eq!(
 6229        editor.update(cx, |editor, cx| editor.text(cx)),
 6230        "one, two\nthree\n"
 6231    );
 6232    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6233
 6234    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6235    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6236
 6237    // Ensure we can still save even if formatting hangs.
 6238    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6239        assert_eq!(
 6240            params.text_document.uri,
 6241            lsp::Url::from_file_path("/file.rs").unwrap()
 6242        );
 6243        futures::future::pending::<()>().await;
 6244        unreachable!()
 6245    });
 6246    let save = editor
 6247        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6248        .unwrap();
 6249    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6250    cx.executor().start_waiting();
 6251    save.await;
 6252    assert_eq!(
 6253        editor.update(cx, |editor, cx| editor.text(cx)),
 6254        "one\ntwo\nthree\n"
 6255    );
 6256    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6257
 6258    // For non-dirty buffer, no formatting request should be sent
 6259    let save = editor
 6260        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6261        .unwrap();
 6262    let _pending_format_request = fake_server
 6263        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6264            panic!("Should not be invoked on non-dirty buffer");
 6265        })
 6266        .next();
 6267    cx.executor().start_waiting();
 6268    save.await;
 6269
 6270    // Set rust language override and assert overridden tabsize is sent to language server
 6271    update_test_language_settings(cx, |settings| {
 6272        settings.languages.insert(
 6273            "Rust".into(),
 6274            LanguageSettingsContent {
 6275                tab_size: NonZeroU32::new(8),
 6276                ..Default::default()
 6277            },
 6278        );
 6279    });
 6280
 6281    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6282    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6283    let save = editor
 6284        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6285        .unwrap();
 6286    fake_server
 6287        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6288            assert_eq!(
 6289                params.text_document.uri,
 6290                lsp::Url::from_file_path("/file.rs").unwrap()
 6291            );
 6292            assert_eq!(params.options.tab_size, 8);
 6293            Ok(Some(vec![]))
 6294        })
 6295        .next()
 6296        .await;
 6297    cx.executor().start_waiting();
 6298    save.await;
 6299}
 6300
 6301#[gpui::test]
 6302async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6303    init_test(cx, |_| {});
 6304
 6305    let cols = 4;
 6306    let rows = 10;
 6307    let sample_text_1 = sample_text(rows, cols, 'a');
 6308    assert_eq!(
 6309        sample_text_1,
 6310        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6311    );
 6312    let sample_text_2 = sample_text(rows, cols, 'l');
 6313    assert_eq!(
 6314        sample_text_2,
 6315        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6316    );
 6317    let sample_text_3 = sample_text(rows, cols, 'v');
 6318    assert_eq!(
 6319        sample_text_3,
 6320        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6321    );
 6322
 6323    let fs = FakeFs::new(cx.executor());
 6324    fs.insert_tree(
 6325        "/a",
 6326        json!({
 6327            "main.rs": sample_text_1,
 6328            "other.rs": sample_text_2,
 6329            "lib.rs": sample_text_3,
 6330        }),
 6331    )
 6332    .await;
 6333
 6334    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6335    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6336    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6337
 6338    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6339    language_registry.add(rust_lang());
 6340    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6341        "Rust",
 6342        FakeLspAdapter {
 6343            capabilities: lsp::ServerCapabilities {
 6344                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6345                ..Default::default()
 6346            },
 6347            ..Default::default()
 6348        },
 6349    );
 6350
 6351    let worktree = project.update(cx, |project, cx| {
 6352        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6353        assert_eq!(worktrees.len(), 1);
 6354        worktrees.pop().unwrap()
 6355    });
 6356    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6357
 6358    let buffer_1 = project
 6359        .update(cx, |project, cx| {
 6360            project.open_buffer((worktree_id, "main.rs"), cx)
 6361        })
 6362        .await
 6363        .unwrap();
 6364    let buffer_2 = project
 6365        .update(cx, |project, cx| {
 6366            project.open_buffer((worktree_id, "other.rs"), cx)
 6367        })
 6368        .await
 6369        .unwrap();
 6370    let buffer_3 = project
 6371        .update(cx, |project, cx| {
 6372            project.open_buffer((worktree_id, "lib.rs"), cx)
 6373        })
 6374        .await
 6375        .unwrap();
 6376
 6377    let multi_buffer = cx.new_model(|cx| {
 6378        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6379        multi_buffer.push_excerpts(
 6380            buffer_1.clone(),
 6381            [
 6382                ExcerptRange {
 6383                    context: Point::new(0, 0)..Point::new(3, 0),
 6384                    primary: None,
 6385                },
 6386                ExcerptRange {
 6387                    context: Point::new(5, 0)..Point::new(7, 0),
 6388                    primary: None,
 6389                },
 6390                ExcerptRange {
 6391                    context: Point::new(9, 0)..Point::new(10, 4),
 6392                    primary: None,
 6393                },
 6394            ],
 6395            cx,
 6396        );
 6397        multi_buffer.push_excerpts(
 6398            buffer_2.clone(),
 6399            [
 6400                ExcerptRange {
 6401                    context: Point::new(0, 0)..Point::new(3, 0),
 6402                    primary: None,
 6403                },
 6404                ExcerptRange {
 6405                    context: Point::new(5, 0)..Point::new(7, 0),
 6406                    primary: None,
 6407                },
 6408                ExcerptRange {
 6409                    context: Point::new(9, 0)..Point::new(10, 4),
 6410                    primary: None,
 6411                },
 6412            ],
 6413            cx,
 6414        );
 6415        multi_buffer.push_excerpts(
 6416            buffer_3.clone(),
 6417            [
 6418                ExcerptRange {
 6419                    context: Point::new(0, 0)..Point::new(3, 0),
 6420                    primary: None,
 6421                },
 6422                ExcerptRange {
 6423                    context: Point::new(5, 0)..Point::new(7, 0),
 6424                    primary: None,
 6425                },
 6426                ExcerptRange {
 6427                    context: Point::new(9, 0)..Point::new(10, 4),
 6428                    primary: None,
 6429                },
 6430            ],
 6431            cx,
 6432        );
 6433        multi_buffer
 6434    });
 6435    let multi_buffer_editor = cx.new_view(|cx| {
 6436        Editor::new(
 6437            EditorMode::Full,
 6438            multi_buffer,
 6439            Some(project.clone()),
 6440            true,
 6441            cx,
 6442        )
 6443    });
 6444
 6445    multi_buffer_editor.update(cx, |editor, cx| {
 6446        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6447        editor.insert("|one|two|three|", cx);
 6448    });
 6449    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6450    multi_buffer_editor.update(cx, |editor, cx| {
 6451        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6452            s.select_ranges(Some(60..70))
 6453        });
 6454        editor.insert("|four|five|six|", cx);
 6455    });
 6456    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6457
 6458    // First two buffers should be edited, but not the third one.
 6459    assert_eq!(
 6460        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6461        "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}",
 6462    );
 6463    buffer_1.update(cx, |buffer, _| {
 6464        assert!(buffer.is_dirty());
 6465        assert_eq!(
 6466            buffer.text(),
 6467            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6468        )
 6469    });
 6470    buffer_2.update(cx, |buffer, _| {
 6471        assert!(buffer.is_dirty());
 6472        assert_eq!(
 6473            buffer.text(),
 6474            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6475        )
 6476    });
 6477    buffer_3.update(cx, |buffer, _| {
 6478        assert!(!buffer.is_dirty());
 6479        assert_eq!(buffer.text(), sample_text_3,)
 6480    });
 6481
 6482    cx.executor().start_waiting();
 6483    let save = multi_buffer_editor
 6484        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6485        .unwrap();
 6486
 6487    let fake_server = fake_servers.next().await.unwrap();
 6488    fake_server
 6489        .server
 6490        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6491            Ok(Some(vec![lsp::TextEdit::new(
 6492                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6493                format!("[{} formatted]", params.text_document.uri),
 6494            )]))
 6495        })
 6496        .detach();
 6497    save.await;
 6498
 6499    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6500    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6501    assert_eq!(
 6502        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6503        "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}",
 6504    );
 6505    buffer_1.update(cx, |buffer, _| {
 6506        assert!(!buffer.is_dirty());
 6507        assert_eq!(
 6508            buffer.text(),
 6509            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6510        )
 6511    });
 6512    buffer_2.update(cx, |buffer, _| {
 6513        assert!(!buffer.is_dirty());
 6514        assert_eq!(
 6515            buffer.text(),
 6516            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6517        )
 6518    });
 6519    buffer_3.update(cx, |buffer, _| {
 6520        assert!(!buffer.is_dirty());
 6521        assert_eq!(buffer.text(), sample_text_3,)
 6522    });
 6523}
 6524
 6525#[gpui::test]
 6526async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6527    init_test(cx, |_| {});
 6528
 6529    let fs = FakeFs::new(cx.executor());
 6530    fs.insert_file("/file.rs", Default::default()).await;
 6531
 6532    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6533
 6534    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6535    language_registry.add(rust_lang());
 6536    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6537        "Rust",
 6538        FakeLspAdapter {
 6539            capabilities: lsp::ServerCapabilities {
 6540                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6541                ..Default::default()
 6542            },
 6543            ..Default::default()
 6544        },
 6545    );
 6546
 6547    let buffer = project
 6548        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6549        .await
 6550        .unwrap();
 6551
 6552    cx.executor().start_waiting();
 6553    let fake_server = fake_servers.next().await.unwrap();
 6554
 6555    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6556    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6557    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6558    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6559
 6560    let save = editor
 6561        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6562        .unwrap();
 6563    fake_server
 6564        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6565            assert_eq!(
 6566                params.text_document.uri,
 6567                lsp::Url::from_file_path("/file.rs").unwrap()
 6568            );
 6569            assert_eq!(params.options.tab_size, 4);
 6570            Ok(Some(vec![lsp::TextEdit::new(
 6571                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6572                ", ".to_string(),
 6573            )]))
 6574        })
 6575        .next()
 6576        .await;
 6577    cx.executor().start_waiting();
 6578    save.await;
 6579    assert_eq!(
 6580        editor.update(cx, |editor, cx| editor.text(cx)),
 6581        "one, two\nthree\n"
 6582    );
 6583    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6584
 6585    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6586    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6587
 6588    // Ensure we can still save even if formatting hangs.
 6589    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6590        move |params, _| async move {
 6591            assert_eq!(
 6592                params.text_document.uri,
 6593                lsp::Url::from_file_path("/file.rs").unwrap()
 6594            );
 6595            futures::future::pending::<()>().await;
 6596            unreachable!()
 6597        },
 6598    );
 6599    let save = editor
 6600        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6601        .unwrap();
 6602    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6603    cx.executor().start_waiting();
 6604    save.await;
 6605    assert_eq!(
 6606        editor.update(cx, |editor, cx| editor.text(cx)),
 6607        "one\ntwo\nthree\n"
 6608    );
 6609    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6610
 6611    // For non-dirty buffer, no formatting request should be sent
 6612    let save = editor
 6613        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6614        .unwrap();
 6615    let _pending_format_request = fake_server
 6616        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6617            panic!("Should not be invoked on non-dirty buffer");
 6618        })
 6619        .next();
 6620    cx.executor().start_waiting();
 6621    save.await;
 6622
 6623    // Set Rust language override and assert overridden tabsize is sent to language server
 6624    update_test_language_settings(cx, |settings| {
 6625        settings.languages.insert(
 6626            "Rust".into(),
 6627            LanguageSettingsContent {
 6628                tab_size: NonZeroU32::new(8),
 6629                ..Default::default()
 6630            },
 6631        );
 6632    });
 6633
 6634    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6635    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6636    let save = editor
 6637        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6638        .unwrap();
 6639    fake_server
 6640        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6641            assert_eq!(
 6642                params.text_document.uri,
 6643                lsp::Url::from_file_path("/file.rs").unwrap()
 6644            );
 6645            assert_eq!(params.options.tab_size, 8);
 6646            Ok(Some(vec![]))
 6647        })
 6648        .next()
 6649        .await;
 6650    cx.executor().start_waiting();
 6651    save.await;
 6652}
 6653
 6654#[gpui::test]
 6655async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6656    init_test(cx, |settings| {
 6657        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6658            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6659        ))
 6660    });
 6661
 6662    let fs = FakeFs::new(cx.executor());
 6663    fs.insert_file("/file.rs", Default::default()).await;
 6664
 6665    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6666
 6667    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6668    language_registry.add(Arc::new(Language::new(
 6669        LanguageConfig {
 6670            name: "Rust".into(),
 6671            matcher: LanguageMatcher {
 6672                path_suffixes: vec!["rs".to_string()],
 6673                ..Default::default()
 6674            },
 6675            ..LanguageConfig::default()
 6676        },
 6677        Some(tree_sitter_rust::language()),
 6678    )));
 6679    update_test_language_settings(cx, |settings| {
 6680        // Enable Prettier formatting for the same buffer, and ensure
 6681        // LSP is called instead of Prettier.
 6682        settings.defaults.prettier = Some(PrettierSettings {
 6683            allowed: true,
 6684            ..PrettierSettings::default()
 6685        });
 6686    });
 6687    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6688        "Rust",
 6689        FakeLspAdapter {
 6690            capabilities: lsp::ServerCapabilities {
 6691                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6692                ..Default::default()
 6693            },
 6694            ..Default::default()
 6695        },
 6696    );
 6697
 6698    let buffer = project
 6699        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6700        .await
 6701        .unwrap();
 6702
 6703    cx.executor().start_waiting();
 6704    let fake_server = fake_servers.next().await.unwrap();
 6705
 6706    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6707    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6708    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6709
 6710    let format = editor
 6711        .update(cx, |editor, cx| {
 6712            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6713        })
 6714        .unwrap();
 6715    fake_server
 6716        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6717            assert_eq!(
 6718                params.text_document.uri,
 6719                lsp::Url::from_file_path("/file.rs").unwrap()
 6720            );
 6721            assert_eq!(params.options.tab_size, 4);
 6722            Ok(Some(vec![lsp::TextEdit::new(
 6723                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6724                ", ".to_string(),
 6725            )]))
 6726        })
 6727        .next()
 6728        .await;
 6729    cx.executor().start_waiting();
 6730    format.await;
 6731    assert_eq!(
 6732        editor.update(cx, |editor, cx| editor.text(cx)),
 6733        "one, two\nthree\n"
 6734    );
 6735
 6736    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6737    // Ensure we don't lock if formatting hangs.
 6738    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6739        assert_eq!(
 6740            params.text_document.uri,
 6741            lsp::Url::from_file_path("/file.rs").unwrap()
 6742        );
 6743        futures::future::pending::<()>().await;
 6744        unreachable!()
 6745    });
 6746    let format = editor
 6747        .update(cx, |editor, cx| {
 6748            editor.perform_format(project, FormatTrigger::Manual, cx)
 6749        })
 6750        .unwrap();
 6751    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6752    cx.executor().start_waiting();
 6753    format.await;
 6754    assert_eq!(
 6755        editor.update(cx, |editor, cx| editor.text(cx)),
 6756        "one\ntwo\nthree\n"
 6757    );
 6758}
 6759
 6760#[gpui::test]
 6761async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6762    init_test(cx, |_| {});
 6763
 6764    let mut cx = EditorLspTestContext::new_rust(
 6765        lsp::ServerCapabilities {
 6766            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6767            ..Default::default()
 6768        },
 6769        cx,
 6770    )
 6771    .await;
 6772
 6773    cx.set_state(indoc! {"
 6774        one.twoˇ
 6775    "});
 6776
 6777    // The format request takes a long time. When it completes, it inserts
 6778    // a newline and an indent before the `.`
 6779    cx.lsp
 6780        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6781            let executor = cx.background_executor().clone();
 6782            async move {
 6783                executor.timer(Duration::from_millis(100)).await;
 6784                Ok(Some(vec![lsp::TextEdit {
 6785                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6786                    new_text: "\n    ".into(),
 6787                }]))
 6788            }
 6789        });
 6790
 6791    // Submit a format request.
 6792    let format_1 = cx
 6793        .update_editor(|editor, cx| editor.format(&Format, cx))
 6794        .unwrap();
 6795    cx.executor().run_until_parked();
 6796
 6797    // Submit a second format request.
 6798    let format_2 = cx
 6799        .update_editor(|editor, cx| editor.format(&Format, cx))
 6800        .unwrap();
 6801    cx.executor().run_until_parked();
 6802
 6803    // Wait for both format requests to complete
 6804    cx.executor().advance_clock(Duration::from_millis(200));
 6805    cx.executor().start_waiting();
 6806    format_1.await.unwrap();
 6807    cx.executor().start_waiting();
 6808    format_2.await.unwrap();
 6809
 6810    // The formatting edits only happens once.
 6811    cx.assert_editor_state(indoc! {"
 6812        one
 6813            .twoˇ
 6814    "});
 6815}
 6816
 6817#[gpui::test]
 6818async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6819    init_test(cx, |settings| {
 6820        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6821    });
 6822
 6823    let mut cx = EditorLspTestContext::new_rust(
 6824        lsp::ServerCapabilities {
 6825            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6826            ..Default::default()
 6827        },
 6828        cx,
 6829    )
 6830    .await;
 6831
 6832    // Set up a buffer white some trailing whitespace and no trailing newline.
 6833    cx.set_state(
 6834        &[
 6835            "one ",   //
 6836            "twoˇ",   //
 6837            "three ", //
 6838            "four",   //
 6839        ]
 6840        .join("\n"),
 6841    );
 6842
 6843    // Submit a format request.
 6844    let format = cx
 6845        .update_editor(|editor, cx| editor.format(&Format, cx))
 6846        .unwrap();
 6847
 6848    // Record which buffer changes have been sent to the language server
 6849    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6850    cx.lsp
 6851        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6852            let buffer_changes = buffer_changes.clone();
 6853            move |params, _| {
 6854                buffer_changes.lock().extend(
 6855                    params
 6856                        .content_changes
 6857                        .into_iter()
 6858                        .map(|e| (e.range.unwrap(), e.text)),
 6859                );
 6860            }
 6861        });
 6862
 6863    // Handle formatting requests to the language server.
 6864    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6865        let buffer_changes = buffer_changes.clone();
 6866        move |_, _| {
 6867            // When formatting is requested, trailing whitespace has already been stripped,
 6868            // and the trailing newline has already been added.
 6869            assert_eq!(
 6870                &buffer_changes.lock()[1..],
 6871                &[
 6872                    (
 6873                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6874                        "".into()
 6875                    ),
 6876                    (
 6877                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6878                        "".into()
 6879                    ),
 6880                    (
 6881                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6882                        "\n".into()
 6883                    ),
 6884                ]
 6885            );
 6886
 6887            // Insert blank lines between each line of the buffer.
 6888            async move {
 6889                Ok(Some(vec![
 6890                    lsp::TextEdit {
 6891                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6892                        new_text: "\n".into(),
 6893                    },
 6894                    lsp::TextEdit {
 6895                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6896                        new_text: "\n".into(),
 6897                    },
 6898                ]))
 6899            }
 6900        }
 6901    });
 6902
 6903    // After formatting the buffer, the trailing whitespace is stripped,
 6904    // a newline is appended, and the edits provided by the language server
 6905    // have been applied.
 6906    format.await.unwrap();
 6907    cx.assert_editor_state(
 6908        &[
 6909            "one",   //
 6910            "",      //
 6911            "twoˇ",  //
 6912            "",      //
 6913            "three", //
 6914            "four",  //
 6915            "",      //
 6916        ]
 6917        .join("\n"),
 6918    );
 6919
 6920    // Undoing the formatting undoes the trailing whitespace removal, the
 6921    // trailing newline, and the LSP edits.
 6922    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6923    cx.assert_editor_state(
 6924        &[
 6925            "one ",   //
 6926            "twoˇ",   //
 6927            "three ", //
 6928            "four",   //
 6929        ]
 6930        .join("\n"),
 6931    );
 6932}
 6933
 6934#[gpui::test]
 6935async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6936    cx: &mut gpui::TestAppContext,
 6937) {
 6938    init_test(cx, |_| {});
 6939
 6940    cx.update(|cx| {
 6941        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6942            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6943                settings.auto_signature_help = Some(true);
 6944            });
 6945        });
 6946    });
 6947
 6948    let mut cx = EditorLspTestContext::new_rust(
 6949        lsp::ServerCapabilities {
 6950            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6951                ..Default::default()
 6952            }),
 6953            ..Default::default()
 6954        },
 6955        cx,
 6956    )
 6957    .await;
 6958
 6959    let language = Language::new(
 6960        LanguageConfig {
 6961            name: "Rust".into(),
 6962            brackets: BracketPairConfig {
 6963                pairs: vec![
 6964                    BracketPair {
 6965                        start: "{".to_string(),
 6966                        end: "}".to_string(),
 6967                        close: true,
 6968                        surround: true,
 6969                        newline: true,
 6970                    },
 6971                    BracketPair {
 6972                        start: "(".to_string(),
 6973                        end: ")".to_string(),
 6974                        close: true,
 6975                        surround: true,
 6976                        newline: true,
 6977                    },
 6978                    BracketPair {
 6979                        start: "/*".to_string(),
 6980                        end: " */".to_string(),
 6981                        close: true,
 6982                        surround: true,
 6983                        newline: true,
 6984                    },
 6985                    BracketPair {
 6986                        start: "[".to_string(),
 6987                        end: "]".to_string(),
 6988                        close: false,
 6989                        surround: false,
 6990                        newline: true,
 6991                    },
 6992                    BracketPair {
 6993                        start: "\"".to_string(),
 6994                        end: "\"".to_string(),
 6995                        close: true,
 6996                        surround: true,
 6997                        newline: false,
 6998                    },
 6999                    BracketPair {
 7000                        start: "<".to_string(),
 7001                        end: ">".to_string(),
 7002                        close: false,
 7003                        surround: true,
 7004                        newline: true,
 7005                    },
 7006                ],
 7007                ..Default::default()
 7008            },
 7009            autoclose_before: "})]".to_string(),
 7010            ..Default::default()
 7011        },
 7012        Some(tree_sitter_rust::language()),
 7013    );
 7014    let language = Arc::new(language);
 7015
 7016    cx.language_registry().add(language.clone());
 7017    cx.update_buffer(|buffer, cx| {
 7018        buffer.set_language(Some(language), cx);
 7019    });
 7020
 7021    cx.set_state(
 7022        &r#"
 7023            fn main() {
 7024                sampleˇ
 7025            }
 7026        "#
 7027        .unindent(),
 7028    );
 7029
 7030    cx.update_editor(|view, cx| {
 7031        view.handle_input("(", cx);
 7032    });
 7033    cx.assert_editor_state(
 7034        &"
 7035            fn main() {
 7036                sample(ˇ)
 7037            }
 7038        "
 7039        .unindent(),
 7040    );
 7041
 7042    let mocked_response = lsp::SignatureHelp {
 7043        signatures: vec![lsp::SignatureInformation {
 7044            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7045            documentation: None,
 7046            parameters: Some(vec![
 7047                lsp::ParameterInformation {
 7048                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7049                    documentation: None,
 7050                },
 7051                lsp::ParameterInformation {
 7052                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7053                    documentation: None,
 7054                },
 7055            ]),
 7056            active_parameter: None,
 7057        }],
 7058        active_signature: Some(0),
 7059        active_parameter: Some(0),
 7060    };
 7061    handle_signature_help_request(&mut cx, mocked_response).await;
 7062
 7063    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7064        .await;
 7065
 7066    cx.editor(|editor, _| {
 7067        let signature_help_state = editor.signature_help_state.popover().cloned();
 7068        assert!(signature_help_state.is_some());
 7069        let ParsedMarkdown {
 7070            text, highlights, ..
 7071        } = signature_help_state.unwrap().parsed_content;
 7072        assert_eq!(text, "param1: u8, param2: u8");
 7073        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7074    });
 7075}
 7076
 7077#[gpui::test]
 7078async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7079    init_test(cx, |_| {});
 7080
 7081    cx.update(|cx| {
 7082        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7083            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7084                settings.auto_signature_help = Some(false);
 7085                settings.show_signature_help_after_edits = Some(false);
 7086            });
 7087        });
 7088    });
 7089
 7090    let mut cx = EditorLspTestContext::new_rust(
 7091        lsp::ServerCapabilities {
 7092            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7093                ..Default::default()
 7094            }),
 7095            ..Default::default()
 7096        },
 7097        cx,
 7098    )
 7099    .await;
 7100
 7101    let language = Language::new(
 7102        LanguageConfig {
 7103            name: "Rust".into(),
 7104            brackets: BracketPairConfig {
 7105                pairs: vec![
 7106                    BracketPair {
 7107                        start: "{".to_string(),
 7108                        end: "}".to_string(),
 7109                        close: true,
 7110                        surround: true,
 7111                        newline: true,
 7112                    },
 7113                    BracketPair {
 7114                        start: "(".to_string(),
 7115                        end: ")".to_string(),
 7116                        close: true,
 7117                        surround: true,
 7118                        newline: true,
 7119                    },
 7120                    BracketPair {
 7121                        start: "/*".to_string(),
 7122                        end: " */".to_string(),
 7123                        close: true,
 7124                        surround: true,
 7125                        newline: true,
 7126                    },
 7127                    BracketPair {
 7128                        start: "[".to_string(),
 7129                        end: "]".to_string(),
 7130                        close: false,
 7131                        surround: false,
 7132                        newline: true,
 7133                    },
 7134                    BracketPair {
 7135                        start: "\"".to_string(),
 7136                        end: "\"".to_string(),
 7137                        close: true,
 7138                        surround: true,
 7139                        newline: false,
 7140                    },
 7141                    BracketPair {
 7142                        start: "<".to_string(),
 7143                        end: ">".to_string(),
 7144                        close: false,
 7145                        surround: true,
 7146                        newline: true,
 7147                    },
 7148                ],
 7149                ..Default::default()
 7150            },
 7151            autoclose_before: "})]".to_string(),
 7152            ..Default::default()
 7153        },
 7154        Some(tree_sitter_rust::language()),
 7155    );
 7156    let language = Arc::new(language);
 7157
 7158    cx.language_registry().add(language.clone());
 7159    cx.update_buffer(|buffer, cx| {
 7160        buffer.set_language(Some(language), cx);
 7161    });
 7162
 7163    // Ensure that signature_help is not called when no signature help is enabled.
 7164    cx.set_state(
 7165        &r#"
 7166            fn main() {
 7167                sampleˇ
 7168            }
 7169        "#
 7170        .unindent(),
 7171    );
 7172    cx.update_editor(|view, cx| {
 7173        view.handle_input("(", cx);
 7174    });
 7175    cx.assert_editor_state(
 7176        &"
 7177            fn main() {
 7178                sample(ˇ)
 7179            }
 7180        "
 7181        .unindent(),
 7182    );
 7183    cx.editor(|editor, _| {
 7184        assert!(editor.signature_help_state.task().is_none());
 7185    });
 7186
 7187    let mocked_response = lsp::SignatureHelp {
 7188        signatures: vec![lsp::SignatureInformation {
 7189            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7190            documentation: None,
 7191            parameters: Some(vec![
 7192                lsp::ParameterInformation {
 7193                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7194                    documentation: None,
 7195                },
 7196                lsp::ParameterInformation {
 7197                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7198                    documentation: None,
 7199                },
 7200            ]),
 7201            active_parameter: None,
 7202        }],
 7203        active_signature: Some(0),
 7204        active_parameter: Some(0),
 7205    };
 7206
 7207    // Ensure that signature_help is called when enabled afte edits
 7208    cx.update(|cx| {
 7209        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7210            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7211                settings.auto_signature_help = Some(false);
 7212                settings.show_signature_help_after_edits = Some(true);
 7213            });
 7214        });
 7215    });
 7216    cx.set_state(
 7217        &r#"
 7218            fn main() {
 7219                sampleˇ
 7220            }
 7221        "#
 7222        .unindent(),
 7223    );
 7224    cx.update_editor(|view, cx| {
 7225        view.handle_input("(", cx);
 7226    });
 7227    cx.assert_editor_state(
 7228        &"
 7229            fn main() {
 7230                sample(ˇ)
 7231            }
 7232        "
 7233        .unindent(),
 7234    );
 7235    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7236    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7237        .await;
 7238    cx.update_editor(|editor, _| {
 7239        let signature_help_state = editor.signature_help_state.popover().cloned();
 7240        assert!(signature_help_state.is_some());
 7241        let ParsedMarkdown {
 7242            text, highlights, ..
 7243        } = signature_help_state.unwrap().parsed_content;
 7244        assert_eq!(text, "param1: u8, param2: u8");
 7245        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7246        editor.signature_help_state = SignatureHelpState::default();
 7247    });
 7248
 7249    // Ensure that signature_help is called when auto signature help override is enabled
 7250    cx.update(|cx| {
 7251        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7252            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7253                settings.auto_signature_help = Some(true);
 7254                settings.show_signature_help_after_edits = Some(false);
 7255            });
 7256        });
 7257    });
 7258    cx.set_state(
 7259        &r#"
 7260            fn main() {
 7261                sampleˇ
 7262            }
 7263        "#
 7264        .unindent(),
 7265    );
 7266    cx.update_editor(|view, cx| {
 7267        view.handle_input("(", cx);
 7268    });
 7269    cx.assert_editor_state(
 7270        &"
 7271            fn main() {
 7272                sample(ˇ)
 7273            }
 7274        "
 7275        .unindent(),
 7276    );
 7277    handle_signature_help_request(&mut cx, mocked_response).await;
 7278    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7279        .await;
 7280    cx.editor(|editor, _| {
 7281        let signature_help_state = editor.signature_help_state.popover().cloned();
 7282        assert!(signature_help_state.is_some());
 7283        let ParsedMarkdown {
 7284            text, highlights, ..
 7285        } = signature_help_state.unwrap().parsed_content;
 7286        assert_eq!(text, "param1: u8, param2: u8");
 7287        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7288    });
 7289}
 7290
 7291#[gpui::test]
 7292async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7293    init_test(cx, |_| {});
 7294    cx.update(|cx| {
 7295        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7296            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7297                settings.auto_signature_help = Some(true);
 7298            });
 7299        });
 7300    });
 7301
 7302    let mut cx = EditorLspTestContext::new_rust(
 7303        lsp::ServerCapabilities {
 7304            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7305                ..Default::default()
 7306            }),
 7307            ..Default::default()
 7308        },
 7309        cx,
 7310    )
 7311    .await;
 7312
 7313    // A test that directly calls `show_signature_help`
 7314    cx.update_editor(|editor, cx| {
 7315        editor.show_signature_help(&ShowSignatureHelp, cx);
 7316    });
 7317
 7318    let mocked_response = lsp::SignatureHelp {
 7319        signatures: vec![lsp::SignatureInformation {
 7320            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7321            documentation: None,
 7322            parameters: Some(vec![
 7323                lsp::ParameterInformation {
 7324                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7325                    documentation: None,
 7326                },
 7327                lsp::ParameterInformation {
 7328                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7329                    documentation: None,
 7330                },
 7331            ]),
 7332            active_parameter: None,
 7333        }],
 7334        active_signature: Some(0),
 7335        active_parameter: Some(0),
 7336    };
 7337    handle_signature_help_request(&mut cx, mocked_response).await;
 7338
 7339    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7340        .await;
 7341
 7342    cx.editor(|editor, _| {
 7343        let signature_help_state = editor.signature_help_state.popover().cloned();
 7344        assert!(signature_help_state.is_some());
 7345        let ParsedMarkdown {
 7346            text, highlights, ..
 7347        } = signature_help_state.unwrap().parsed_content;
 7348        assert_eq!(text, "param1: u8, param2: u8");
 7349        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7350    });
 7351
 7352    // When exiting outside from inside the brackets, `signature_help` is closed.
 7353    cx.set_state(indoc! {"
 7354        fn main() {
 7355            sample(ˇ);
 7356        }
 7357
 7358        fn sample(param1: u8, param2: u8) {}
 7359    "});
 7360
 7361    cx.update_editor(|editor, cx| {
 7362        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7363    });
 7364
 7365    let mocked_response = lsp::SignatureHelp {
 7366        signatures: Vec::new(),
 7367        active_signature: None,
 7368        active_parameter: None,
 7369    };
 7370    handle_signature_help_request(&mut cx, mocked_response).await;
 7371
 7372    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7373        .await;
 7374
 7375    cx.editor(|editor, _| {
 7376        assert!(!editor.signature_help_state.is_shown());
 7377    });
 7378
 7379    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7380    cx.set_state(indoc! {"
 7381        fn main() {
 7382            sample(ˇ);
 7383        }
 7384
 7385        fn sample(param1: u8, param2: u8) {}
 7386    "});
 7387
 7388    let mocked_response = lsp::SignatureHelp {
 7389        signatures: vec![lsp::SignatureInformation {
 7390            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7391            documentation: None,
 7392            parameters: Some(vec![
 7393                lsp::ParameterInformation {
 7394                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7395                    documentation: None,
 7396                },
 7397                lsp::ParameterInformation {
 7398                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7399                    documentation: None,
 7400                },
 7401            ]),
 7402            active_parameter: None,
 7403        }],
 7404        active_signature: Some(0),
 7405        active_parameter: Some(0),
 7406    };
 7407    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7408    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7409        .await;
 7410    cx.editor(|editor, _| {
 7411        assert!(editor.signature_help_state.is_shown());
 7412    });
 7413
 7414    // Restore the popover with more parameter input
 7415    cx.set_state(indoc! {"
 7416        fn main() {
 7417            sample(param1, param2ˇ);
 7418        }
 7419
 7420        fn sample(param1: u8, param2: u8) {}
 7421    "});
 7422
 7423    let mocked_response = lsp::SignatureHelp {
 7424        signatures: vec![lsp::SignatureInformation {
 7425            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7426            documentation: None,
 7427            parameters: Some(vec![
 7428                lsp::ParameterInformation {
 7429                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7430                    documentation: None,
 7431                },
 7432                lsp::ParameterInformation {
 7433                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7434                    documentation: None,
 7435                },
 7436            ]),
 7437            active_parameter: None,
 7438        }],
 7439        active_signature: Some(0),
 7440        active_parameter: Some(1),
 7441    };
 7442    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7443    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7444        .await;
 7445
 7446    // When selecting a range, the popover is gone.
 7447    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7448    cx.update_editor(|editor, cx| {
 7449        editor.change_selections(None, cx, |s| {
 7450            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7451        })
 7452    });
 7453    cx.assert_editor_state(indoc! {"
 7454        fn main() {
 7455            sample(param1, «ˇparam2»);
 7456        }
 7457
 7458        fn sample(param1: u8, param2: u8) {}
 7459    "});
 7460    cx.editor(|editor, _| {
 7461        assert!(!editor.signature_help_state.is_shown());
 7462    });
 7463
 7464    // When unselecting again, the popover is back if within the brackets.
 7465    cx.update_editor(|editor, cx| {
 7466        editor.change_selections(None, cx, |s| {
 7467            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7468        })
 7469    });
 7470    cx.assert_editor_state(indoc! {"
 7471        fn main() {
 7472            sample(param1, ˇparam2);
 7473        }
 7474
 7475        fn sample(param1: u8, param2: u8) {}
 7476    "});
 7477    handle_signature_help_request(&mut cx, mocked_response).await;
 7478    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7479        .await;
 7480    cx.editor(|editor, _| {
 7481        assert!(editor.signature_help_state.is_shown());
 7482    });
 7483
 7484    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7485    cx.update_editor(|editor, cx| {
 7486        editor.change_selections(None, cx, |s| {
 7487            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7488            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7489        })
 7490    });
 7491    cx.assert_editor_state(indoc! {"
 7492        fn main() {
 7493            sample(param1, ˇparam2);
 7494        }
 7495
 7496        fn sample(param1: u8, param2: u8) {}
 7497    "});
 7498
 7499    let mocked_response = lsp::SignatureHelp {
 7500        signatures: vec![lsp::SignatureInformation {
 7501            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7502            documentation: None,
 7503            parameters: Some(vec![
 7504                lsp::ParameterInformation {
 7505                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7506                    documentation: None,
 7507                },
 7508                lsp::ParameterInformation {
 7509                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7510                    documentation: None,
 7511                },
 7512            ]),
 7513            active_parameter: None,
 7514        }],
 7515        active_signature: Some(0),
 7516        active_parameter: Some(1),
 7517    };
 7518    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7519    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7520        .await;
 7521    cx.update_editor(|editor, cx| {
 7522        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7523    });
 7524    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7525        .await;
 7526    cx.update_editor(|editor, cx| {
 7527        editor.change_selections(None, cx, |s| {
 7528            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7529        })
 7530    });
 7531    cx.assert_editor_state(indoc! {"
 7532        fn main() {
 7533            sample(param1, «ˇparam2»);
 7534        }
 7535
 7536        fn sample(param1: u8, param2: u8) {}
 7537    "});
 7538    cx.update_editor(|editor, cx| {
 7539        editor.change_selections(None, cx, |s| {
 7540            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7541        })
 7542    });
 7543    cx.assert_editor_state(indoc! {"
 7544        fn main() {
 7545            sample(param1, ˇparam2);
 7546        }
 7547
 7548        fn sample(param1: u8, param2: u8) {}
 7549    "});
 7550    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7551        .await;
 7552}
 7553
 7554#[gpui::test]
 7555async fn test_completion(cx: &mut gpui::TestAppContext) {
 7556    init_test(cx, |_| {});
 7557
 7558    let mut cx = EditorLspTestContext::new_rust(
 7559        lsp::ServerCapabilities {
 7560            completion_provider: Some(lsp::CompletionOptions {
 7561                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7562                resolve_provider: Some(true),
 7563                ..Default::default()
 7564            }),
 7565            ..Default::default()
 7566        },
 7567        cx,
 7568    )
 7569    .await;
 7570    let counter = Arc::new(AtomicUsize::new(0));
 7571
 7572    cx.set_state(indoc! {"
 7573        oneˇ
 7574        two
 7575        three
 7576    "});
 7577    cx.simulate_keystroke(".");
 7578    handle_completion_request(
 7579        &mut cx,
 7580        indoc! {"
 7581            one.|<>
 7582            two
 7583            three
 7584        "},
 7585        vec!["first_completion", "second_completion"],
 7586        counter.clone(),
 7587    )
 7588    .await;
 7589    cx.condition(|editor, _| editor.context_menu_visible())
 7590        .await;
 7591    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7592
 7593    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7594        editor.context_menu_next(&Default::default(), cx);
 7595        editor
 7596            .confirm_completion(&ConfirmCompletion::default(), cx)
 7597            .unwrap()
 7598    });
 7599    cx.assert_editor_state(indoc! {"
 7600        one.second_completionˇ
 7601        two
 7602        three
 7603    "});
 7604
 7605    handle_resolve_completion_request(
 7606        &mut cx,
 7607        Some(vec![
 7608            (
 7609                //This overlaps with the primary completion edit which is
 7610                //misbehavior from the LSP spec, test that we filter it out
 7611                indoc! {"
 7612                    one.second_ˇcompletion
 7613                    two
 7614                    threeˇ
 7615                "},
 7616                "overlapping additional edit",
 7617            ),
 7618            (
 7619                indoc! {"
 7620                    one.second_completion
 7621                    two
 7622                    threeˇ
 7623                "},
 7624                "\nadditional edit",
 7625            ),
 7626        ]),
 7627    )
 7628    .await;
 7629    apply_additional_edits.await.unwrap();
 7630    cx.assert_editor_state(indoc! {"
 7631        one.second_completionˇ
 7632        two
 7633        three
 7634        additional edit
 7635    "});
 7636
 7637    cx.set_state(indoc! {"
 7638        one.second_completion
 7639        twoˇ
 7640        threeˇ
 7641        additional edit
 7642    "});
 7643    cx.simulate_keystroke(" ");
 7644    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7645    cx.simulate_keystroke("s");
 7646    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7647
 7648    cx.assert_editor_state(indoc! {"
 7649        one.second_completion
 7650        two sˇ
 7651        three sˇ
 7652        additional edit
 7653    "});
 7654    handle_completion_request(
 7655        &mut cx,
 7656        indoc! {"
 7657            one.second_completion
 7658            two s
 7659            three <s|>
 7660            additional edit
 7661        "},
 7662        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7663        counter.clone(),
 7664    )
 7665    .await;
 7666    cx.condition(|editor, _| editor.context_menu_visible())
 7667        .await;
 7668    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7669
 7670    cx.simulate_keystroke("i");
 7671
 7672    handle_completion_request(
 7673        &mut cx,
 7674        indoc! {"
 7675            one.second_completion
 7676            two si
 7677            three <si|>
 7678            additional edit
 7679        "},
 7680        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7681        counter.clone(),
 7682    )
 7683    .await;
 7684    cx.condition(|editor, _| editor.context_menu_visible())
 7685        .await;
 7686    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7687
 7688    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7689        editor
 7690            .confirm_completion(&ConfirmCompletion::default(), cx)
 7691            .unwrap()
 7692    });
 7693    cx.assert_editor_state(indoc! {"
 7694        one.second_completion
 7695        two sixth_completionˇ
 7696        three sixth_completionˇ
 7697        additional edit
 7698    "});
 7699
 7700    handle_resolve_completion_request(&mut cx, None).await;
 7701    apply_additional_edits.await.unwrap();
 7702
 7703    _ = cx.update(|cx| {
 7704        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7705            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7706                settings.show_completions_on_input = Some(false);
 7707            });
 7708        })
 7709    });
 7710    cx.set_state("editorˇ");
 7711    cx.simulate_keystroke(".");
 7712    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7713    cx.simulate_keystroke("c");
 7714    cx.simulate_keystroke("l");
 7715    cx.simulate_keystroke("o");
 7716    cx.assert_editor_state("editor.cloˇ");
 7717    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7718    cx.update_editor(|editor, cx| {
 7719        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7720    });
 7721    handle_completion_request(
 7722        &mut cx,
 7723        "editor.<clo|>",
 7724        vec!["close", "clobber"],
 7725        counter.clone(),
 7726    )
 7727    .await;
 7728    cx.condition(|editor, _| editor.context_menu_visible())
 7729        .await;
 7730    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7731
 7732    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7733        editor
 7734            .confirm_completion(&ConfirmCompletion::default(), cx)
 7735            .unwrap()
 7736    });
 7737    cx.assert_editor_state("editor.closeˇ");
 7738    handle_resolve_completion_request(&mut cx, None).await;
 7739    apply_additional_edits.await.unwrap();
 7740}
 7741
 7742#[gpui::test]
 7743async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7744    init_test(cx, |_| {});
 7745    let mut cx = EditorLspTestContext::new_rust(
 7746        lsp::ServerCapabilities {
 7747            completion_provider: Some(lsp::CompletionOptions {
 7748                trigger_characters: Some(vec![".".to_string()]),
 7749                ..Default::default()
 7750            }),
 7751            ..Default::default()
 7752        },
 7753        cx,
 7754    )
 7755    .await;
 7756    cx.lsp
 7757        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7758            Ok(Some(lsp::CompletionResponse::Array(vec![
 7759                lsp::CompletionItem {
 7760                    label: "first".into(),
 7761                    ..Default::default()
 7762                },
 7763                lsp::CompletionItem {
 7764                    label: "last".into(),
 7765                    ..Default::default()
 7766                },
 7767            ])))
 7768        });
 7769    cx.set_state("variableˇ");
 7770    cx.simulate_keystroke(".");
 7771    cx.executor().run_until_parked();
 7772
 7773    cx.update_editor(|editor, _| {
 7774        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7775            assert_eq!(
 7776                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7777                &["first", "last"]
 7778            );
 7779        } else {
 7780            panic!("expected completion menu to be open");
 7781        }
 7782    });
 7783
 7784    cx.update_editor(|editor, cx| {
 7785        editor.move_page_down(&MovePageDown::default(), cx);
 7786        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7787            assert!(
 7788                menu.selected_item == 1,
 7789                "expected PageDown to select the last item from the context menu"
 7790            );
 7791        } else {
 7792            panic!("expected completion menu to stay open after PageDown");
 7793        }
 7794    });
 7795
 7796    cx.update_editor(|editor, cx| {
 7797        editor.move_page_up(&MovePageUp::default(), cx);
 7798        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7799            assert!(
 7800                menu.selected_item == 0,
 7801                "expected PageUp to select the first item from the context menu"
 7802            );
 7803        } else {
 7804            panic!("expected completion menu to stay open after PageUp");
 7805        }
 7806    });
 7807}
 7808
 7809#[gpui::test]
 7810async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7811    init_test(cx, |_| {});
 7812
 7813    let mut cx = EditorLspTestContext::new_rust(
 7814        lsp::ServerCapabilities {
 7815            completion_provider: Some(lsp::CompletionOptions {
 7816                trigger_characters: Some(vec![".".to_string()]),
 7817                resolve_provider: Some(true),
 7818                ..Default::default()
 7819            }),
 7820            ..Default::default()
 7821        },
 7822        cx,
 7823    )
 7824    .await;
 7825
 7826    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7827    cx.simulate_keystroke(".");
 7828    let completion_item = lsp::CompletionItem {
 7829        label: "Some".into(),
 7830        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7831        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7832        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7833            kind: lsp::MarkupKind::Markdown,
 7834            value: "```rust\nSome(2)\n```".to_string(),
 7835        })),
 7836        deprecated: Some(false),
 7837        sort_text: Some("Some".to_string()),
 7838        filter_text: Some("Some".to_string()),
 7839        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7840        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7841            range: lsp::Range {
 7842                start: lsp::Position {
 7843                    line: 0,
 7844                    character: 22,
 7845                },
 7846                end: lsp::Position {
 7847                    line: 0,
 7848                    character: 22,
 7849                },
 7850            },
 7851            new_text: "Some(2)".to_string(),
 7852        })),
 7853        additional_text_edits: Some(vec![lsp::TextEdit {
 7854            range: lsp::Range {
 7855                start: lsp::Position {
 7856                    line: 0,
 7857                    character: 20,
 7858                },
 7859                end: lsp::Position {
 7860                    line: 0,
 7861                    character: 22,
 7862                },
 7863            },
 7864            new_text: "".to_string(),
 7865        }]),
 7866        ..Default::default()
 7867    };
 7868
 7869    let closure_completion_item = completion_item.clone();
 7870    let counter = Arc::new(AtomicUsize::new(0));
 7871    let counter_clone = counter.clone();
 7872    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7873        let task_completion_item = closure_completion_item.clone();
 7874        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7875        async move {
 7876            Ok(Some(lsp::CompletionResponse::Array(vec![
 7877                task_completion_item,
 7878            ])))
 7879        }
 7880    });
 7881
 7882    cx.condition(|editor, _| editor.context_menu_visible())
 7883        .await;
 7884    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7885    assert!(request.next().await.is_some());
 7886    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7887
 7888    cx.simulate_keystroke("S");
 7889    cx.simulate_keystroke("o");
 7890    cx.simulate_keystroke("m");
 7891    cx.condition(|editor, _| editor.context_menu_visible())
 7892        .await;
 7893    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7894    assert!(request.next().await.is_some());
 7895    assert!(request.next().await.is_some());
 7896    assert!(request.next().await.is_some());
 7897    request.close();
 7898    assert!(request.next().await.is_none());
 7899    assert_eq!(
 7900        counter.load(atomic::Ordering::Acquire),
 7901        4,
 7902        "With the completions menu open, only one LSP request should happen per input"
 7903    );
 7904}
 7905
 7906#[gpui::test]
 7907async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7908    init_test(cx, |_| {});
 7909    let mut cx = EditorTestContext::new(cx).await;
 7910    let language = Arc::new(Language::new(
 7911        LanguageConfig {
 7912            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7913            ..Default::default()
 7914        },
 7915        Some(tree_sitter_rust::language()),
 7916    ));
 7917    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7918
 7919    // If multiple selections intersect a line, the line is only toggled once.
 7920    cx.set_state(indoc! {"
 7921        fn a() {
 7922            «//b();
 7923            ˇ»// «c();
 7924            //ˇ»  d();
 7925        }
 7926    "});
 7927
 7928    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7929
 7930    cx.assert_editor_state(indoc! {"
 7931        fn a() {
 7932            «b();
 7933            c();
 7934            ˇ» d();
 7935        }
 7936    "});
 7937
 7938    // The comment prefix is inserted at the same column for every line in a
 7939    // selection.
 7940    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7941
 7942    cx.assert_editor_state(indoc! {"
 7943        fn a() {
 7944            // «b();
 7945            // c();
 7946            ˇ»//  d();
 7947        }
 7948    "});
 7949
 7950    // If a selection ends at the beginning of a line, that line is not toggled.
 7951    cx.set_selections_state(indoc! {"
 7952        fn a() {
 7953            // b();
 7954            «// c();
 7955        ˇ»    //  d();
 7956        }
 7957    "});
 7958
 7959    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7960
 7961    cx.assert_editor_state(indoc! {"
 7962        fn a() {
 7963            // b();
 7964            «c();
 7965        ˇ»    //  d();
 7966        }
 7967    "});
 7968
 7969    // If a selection span a single line and is empty, the line is toggled.
 7970    cx.set_state(indoc! {"
 7971        fn a() {
 7972            a();
 7973            b();
 7974        ˇ
 7975        }
 7976    "});
 7977
 7978    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7979
 7980    cx.assert_editor_state(indoc! {"
 7981        fn a() {
 7982            a();
 7983            b();
 7984        //•ˇ
 7985        }
 7986    "});
 7987
 7988    // If a selection span multiple lines, empty lines are not toggled.
 7989    cx.set_state(indoc! {"
 7990        fn a() {
 7991            «a();
 7992
 7993            c();ˇ»
 7994        }
 7995    "});
 7996
 7997    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7998
 7999    cx.assert_editor_state(indoc! {"
 8000        fn a() {
 8001            // «a();
 8002
 8003            // c();ˇ»
 8004        }
 8005    "});
 8006
 8007    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8008    cx.set_state(indoc! {"
 8009        fn a() {
 8010            «// a();
 8011            /// b();
 8012            //! c();ˇ»
 8013        }
 8014    "});
 8015
 8016    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8017
 8018    cx.assert_editor_state(indoc! {"
 8019        fn a() {
 8020            «a();
 8021            b();
 8022            c();ˇ»
 8023        }
 8024    "});
 8025}
 8026
 8027#[gpui::test]
 8028async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8029    init_test(cx, |_| {});
 8030
 8031    let language = Arc::new(Language::new(
 8032        LanguageConfig {
 8033            line_comments: vec!["// ".into()],
 8034            ..Default::default()
 8035        },
 8036        Some(tree_sitter_rust::language()),
 8037    ));
 8038
 8039    let mut cx = EditorTestContext::new(cx).await;
 8040
 8041    cx.language_registry().add(language.clone());
 8042    cx.update_buffer(|buffer, cx| {
 8043        buffer.set_language(Some(language), cx);
 8044    });
 8045
 8046    let toggle_comments = &ToggleComments {
 8047        advance_downwards: true,
 8048    };
 8049
 8050    // Single cursor on one line -> advance
 8051    // Cursor moves horizontally 3 characters as well on non-blank line
 8052    cx.set_state(indoc!(
 8053        "fn a() {
 8054             ˇdog();
 8055             cat();
 8056        }"
 8057    ));
 8058    cx.update_editor(|editor, cx| {
 8059        editor.toggle_comments(toggle_comments, cx);
 8060    });
 8061    cx.assert_editor_state(indoc!(
 8062        "fn a() {
 8063             // dog();
 8064             catˇ();
 8065        }"
 8066    ));
 8067
 8068    // Single selection on one line -> don't advance
 8069    cx.set_state(indoc!(
 8070        "fn a() {
 8071             «dog()ˇ»;
 8072             cat();
 8073        }"
 8074    ));
 8075    cx.update_editor(|editor, cx| {
 8076        editor.toggle_comments(toggle_comments, cx);
 8077    });
 8078    cx.assert_editor_state(indoc!(
 8079        "fn a() {
 8080             // «dog()ˇ»;
 8081             cat();
 8082        }"
 8083    ));
 8084
 8085    // Multiple cursors on one line -> advance
 8086    cx.set_state(indoc!(
 8087        "fn a() {
 8088             ˇdˇog();
 8089             cat();
 8090        }"
 8091    ));
 8092    cx.update_editor(|editor, cx| {
 8093        editor.toggle_comments(toggle_comments, cx);
 8094    });
 8095    cx.assert_editor_state(indoc!(
 8096        "fn a() {
 8097             // dog();
 8098             catˇ(ˇ);
 8099        }"
 8100    ));
 8101
 8102    // Multiple cursors on one line, with selection -> don't advance
 8103    cx.set_state(indoc!(
 8104        "fn a() {
 8105             ˇdˇog«()ˇ»;
 8106             cat();
 8107        }"
 8108    ));
 8109    cx.update_editor(|editor, cx| {
 8110        editor.toggle_comments(toggle_comments, cx);
 8111    });
 8112    cx.assert_editor_state(indoc!(
 8113        "fn a() {
 8114             // ˇdˇog«()ˇ»;
 8115             cat();
 8116        }"
 8117    ));
 8118
 8119    // Single cursor on one line -> advance
 8120    // Cursor moves to column 0 on blank line
 8121    cx.set_state(indoc!(
 8122        "fn a() {
 8123             ˇdog();
 8124
 8125             cat();
 8126        }"
 8127    ));
 8128    cx.update_editor(|editor, cx| {
 8129        editor.toggle_comments(toggle_comments, cx);
 8130    });
 8131    cx.assert_editor_state(indoc!(
 8132        "fn a() {
 8133             // dog();
 8134        ˇ
 8135             cat();
 8136        }"
 8137    ));
 8138
 8139    // Single cursor on one line -> advance
 8140    // Cursor starts and ends at column 0
 8141    cx.set_state(indoc!(
 8142        "fn a() {
 8143         ˇ    dog();
 8144             cat();
 8145        }"
 8146    ));
 8147    cx.update_editor(|editor, cx| {
 8148        editor.toggle_comments(toggle_comments, cx);
 8149    });
 8150    cx.assert_editor_state(indoc!(
 8151        "fn a() {
 8152             // dog();
 8153         ˇ    cat();
 8154        }"
 8155    ));
 8156}
 8157
 8158#[gpui::test]
 8159async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8160    init_test(cx, |_| {});
 8161
 8162    let mut cx = EditorTestContext::new(cx).await;
 8163
 8164    let html_language = Arc::new(
 8165        Language::new(
 8166            LanguageConfig {
 8167                name: "HTML".into(),
 8168                block_comment: Some(("<!-- ".into(), " -->".into())),
 8169                ..Default::default()
 8170            },
 8171            Some(tree_sitter_html::language()),
 8172        )
 8173        .with_injection_query(
 8174            r#"
 8175            (script_element
 8176                (raw_text) @content
 8177                (#set! "language" "javascript"))
 8178            "#,
 8179        )
 8180        .unwrap(),
 8181    );
 8182
 8183    let javascript_language = Arc::new(Language::new(
 8184        LanguageConfig {
 8185            name: "JavaScript".into(),
 8186            line_comments: vec!["// ".into()],
 8187            ..Default::default()
 8188        },
 8189        Some(tree_sitter_typescript::language_tsx()),
 8190    ));
 8191
 8192    cx.language_registry().add(html_language.clone());
 8193    cx.language_registry().add(javascript_language.clone());
 8194    cx.update_buffer(|buffer, cx| {
 8195        buffer.set_language(Some(html_language), cx);
 8196    });
 8197
 8198    // Toggle comments for empty selections
 8199    cx.set_state(
 8200        &r#"
 8201            <p>A</p>ˇ
 8202            <p>B</p>ˇ
 8203            <p>C</p>ˇ
 8204        "#
 8205        .unindent(),
 8206    );
 8207    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8208    cx.assert_editor_state(
 8209        &r#"
 8210            <!-- <p>A</p>ˇ -->
 8211            <!-- <p>B</p>ˇ -->
 8212            <!-- <p>C</p>ˇ -->
 8213        "#
 8214        .unindent(),
 8215    );
 8216    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8217    cx.assert_editor_state(
 8218        &r#"
 8219            <p>A</p>ˇ
 8220            <p>B</p>ˇ
 8221            <p>C</p>ˇ
 8222        "#
 8223        .unindent(),
 8224    );
 8225
 8226    // Toggle comments for mixture of empty and non-empty selections, where
 8227    // multiple selections occupy a given line.
 8228    cx.set_state(
 8229        &r#"
 8230            <p>A«</p>
 8231            <p>ˇ»B</p>ˇ
 8232            <p>C«</p>
 8233            <p>ˇ»D</p>ˇ
 8234        "#
 8235        .unindent(),
 8236    );
 8237
 8238    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8239    cx.assert_editor_state(
 8240        &r#"
 8241            <!-- <p>A«</p>
 8242            <p>ˇ»B</p>ˇ -->
 8243            <!-- <p>C«</p>
 8244            <p>ˇ»D</p>ˇ -->
 8245        "#
 8246        .unindent(),
 8247    );
 8248    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8249    cx.assert_editor_state(
 8250        &r#"
 8251            <p>A«</p>
 8252            <p>ˇ»B</p>ˇ
 8253            <p>C«</p>
 8254            <p>ˇ»D</p>ˇ
 8255        "#
 8256        .unindent(),
 8257    );
 8258
 8259    // Toggle comments when different languages are active for different
 8260    // selections.
 8261    cx.set_state(
 8262        &r#"
 8263            ˇ<script>
 8264                ˇvar x = new Y();
 8265            ˇ</script>
 8266        "#
 8267        .unindent(),
 8268    );
 8269    cx.executor().run_until_parked();
 8270    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8271    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8272    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8273    cx.assert_editor_state(
 8274        &r#"
 8275            <!-- ˇ<script> -->
 8276                // ˇvar x = new Y();
 8277            // ˇ</script>
 8278        "#
 8279        .unindent(),
 8280    );
 8281}
 8282
 8283#[gpui::test]
 8284fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8285    init_test(cx, |_| {});
 8286
 8287    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8288    let multibuffer = cx.new_model(|cx| {
 8289        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8290        multibuffer.push_excerpts(
 8291            buffer.clone(),
 8292            [
 8293                ExcerptRange {
 8294                    context: Point::new(0, 0)..Point::new(0, 4),
 8295                    primary: None,
 8296                },
 8297                ExcerptRange {
 8298                    context: Point::new(1, 0)..Point::new(1, 4),
 8299                    primary: None,
 8300                },
 8301            ],
 8302            cx,
 8303        );
 8304        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8305        multibuffer
 8306    });
 8307
 8308    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8309    _ = view.update(cx, |view, cx| {
 8310        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8311        view.change_selections(None, cx, |s| {
 8312            s.select_ranges([
 8313                Point::new(0, 0)..Point::new(0, 0),
 8314                Point::new(1, 0)..Point::new(1, 0),
 8315            ])
 8316        });
 8317
 8318        view.handle_input("X", cx);
 8319        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8320        assert_eq!(
 8321            view.selections.ranges(cx),
 8322            [
 8323                Point::new(0, 1)..Point::new(0, 1),
 8324                Point::new(1, 1)..Point::new(1, 1),
 8325            ]
 8326        );
 8327
 8328        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8329        view.change_selections(None, cx, |s| {
 8330            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8331        });
 8332        view.backspace(&Default::default(), cx);
 8333        assert_eq!(view.text(cx), "Xa\nbbb");
 8334        assert_eq!(
 8335            view.selections.ranges(cx),
 8336            [Point::new(1, 0)..Point::new(1, 0)]
 8337        );
 8338
 8339        view.change_selections(None, cx, |s| {
 8340            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8341        });
 8342        view.backspace(&Default::default(), cx);
 8343        assert_eq!(view.text(cx), "X\nbb");
 8344        assert_eq!(
 8345            view.selections.ranges(cx),
 8346            [Point::new(0, 1)..Point::new(0, 1)]
 8347        );
 8348    });
 8349}
 8350
 8351#[gpui::test]
 8352fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8353    init_test(cx, |_| {});
 8354
 8355    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8356    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8357        indoc! {"
 8358            [aaaa
 8359            (bbbb]
 8360            cccc)",
 8361        },
 8362        markers.clone(),
 8363    );
 8364    let excerpt_ranges = markers.into_iter().map(|marker| {
 8365        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8366        ExcerptRange {
 8367            context,
 8368            primary: None,
 8369        }
 8370    });
 8371    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8372    let multibuffer = cx.new_model(|cx| {
 8373        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8374        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8375        multibuffer
 8376    });
 8377
 8378    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8379    _ = view.update(cx, |view, cx| {
 8380        let (expected_text, selection_ranges) = marked_text_ranges(
 8381            indoc! {"
 8382                aaaa
 8383                bˇbbb
 8384                bˇbbˇb
 8385                cccc"
 8386            },
 8387            true,
 8388        );
 8389        assert_eq!(view.text(cx), expected_text);
 8390        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8391
 8392        view.handle_input("X", cx);
 8393
 8394        let (expected_text, expected_selections) = marked_text_ranges(
 8395            indoc! {"
 8396                aaaa
 8397                bXˇbbXb
 8398                bXˇbbXˇb
 8399                cccc"
 8400            },
 8401            false,
 8402        );
 8403        assert_eq!(view.text(cx), expected_text);
 8404        assert_eq!(view.selections.ranges(cx), expected_selections);
 8405
 8406        view.newline(&Newline, cx);
 8407        let (expected_text, expected_selections) = marked_text_ranges(
 8408            indoc! {"
 8409                aaaa
 8410                bX
 8411                ˇbbX
 8412                b
 8413                bX
 8414                ˇbbX
 8415                ˇb
 8416                cccc"
 8417            },
 8418            false,
 8419        );
 8420        assert_eq!(view.text(cx), expected_text);
 8421        assert_eq!(view.selections.ranges(cx), expected_selections);
 8422    });
 8423}
 8424
 8425#[gpui::test]
 8426fn test_refresh_selections(cx: &mut TestAppContext) {
 8427    init_test(cx, |_| {});
 8428
 8429    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8430    let mut excerpt1_id = None;
 8431    let multibuffer = cx.new_model(|cx| {
 8432        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8433        excerpt1_id = multibuffer
 8434            .push_excerpts(
 8435                buffer.clone(),
 8436                [
 8437                    ExcerptRange {
 8438                        context: Point::new(0, 0)..Point::new(1, 4),
 8439                        primary: None,
 8440                    },
 8441                    ExcerptRange {
 8442                        context: Point::new(1, 0)..Point::new(2, 4),
 8443                        primary: None,
 8444                    },
 8445                ],
 8446                cx,
 8447            )
 8448            .into_iter()
 8449            .next();
 8450        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8451        multibuffer
 8452    });
 8453
 8454    let editor = cx.add_window(|cx| {
 8455        let mut editor = build_editor(multibuffer.clone(), cx);
 8456        let snapshot = editor.snapshot(cx);
 8457        editor.change_selections(None, cx, |s| {
 8458            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8459        });
 8460        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8461        assert_eq!(
 8462            editor.selections.ranges(cx),
 8463            [
 8464                Point::new(1, 3)..Point::new(1, 3),
 8465                Point::new(2, 1)..Point::new(2, 1),
 8466            ]
 8467        );
 8468        editor
 8469    });
 8470
 8471    // Refreshing selections is a no-op when excerpts haven't changed.
 8472    _ = editor.update(cx, |editor, cx| {
 8473        editor.change_selections(None, cx, |s| s.refresh());
 8474        assert_eq!(
 8475            editor.selections.ranges(cx),
 8476            [
 8477                Point::new(1, 3)..Point::new(1, 3),
 8478                Point::new(2, 1)..Point::new(2, 1),
 8479            ]
 8480        );
 8481    });
 8482
 8483    _ = multibuffer.update(cx, |multibuffer, cx| {
 8484        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8485    });
 8486    _ = editor.update(cx, |editor, cx| {
 8487        // Removing an excerpt causes the first selection to become degenerate.
 8488        assert_eq!(
 8489            editor.selections.ranges(cx),
 8490            [
 8491                Point::new(0, 0)..Point::new(0, 0),
 8492                Point::new(0, 1)..Point::new(0, 1)
 8493            ]
 8494        );
 8495
 8496        // Refreshing selections will relocate the first selection to the original buffer
 8497        // location.
 8498        editor.change_selections(None, cx, |s| s.refresh());
 8499        assert_eq!(
 8500            editor.selections.ranges(cx),
 8501            [
 8502                Point::new(0, 1)..Point::new(0, 1),
 8503                Point::new(0, 3)..Point::new(0, 3)
 8504            ]
 8505        );
 8506        assert!(editor.selections.pending_anchor().is_some());
 8507    });
 8508}
 8509
 8510#[gpui::test]
 8511fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8512    init_test(cx, |_| {});
 8513
 8514    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8515    let mut excerpt1_id = None;
 8516    let multibuffer = cx.new_model(|cx| {
 8517        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8518        excerpt1_id = multibuffer
 8519            .push_excerpts(
 8520                buffer.clone(),
 8521                [
 8522                    ExcerptRange {
 8523                        context: Point::new(0, 0)..Point::new(1, 4),
 8524                        primary: None,
 8525                    },
 8526                    ExcerptRange {
 8527                        context: Point::new(1, 0)..Point::new(2, 4),
 8528                        primary: None,
 8529                    },
 8530                ],
 8531                cx,
 8532            )
 8533            .into_iter()
 8534            .next();
 8535        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8536        multibuffer
 8537    });
 8538
 8539    let editor = cx.add_window(|cx| {
 8540        let mut editor = build_editor(multibuffer.clone(), cx);
 8541        let snapshot = editor.snapshot(cx);
 8542        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8543        assert_eq!(
 8544            editor.selections.ranges(cx),
 8545            [Point::new(1, 3)..Point::new(1, 3)]
 8546        );
 8547        editor
 8548    });
 8549
 8550    _ = multibuffer.update(cx, |multibuffer, cx| {
 8551        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8552    });
 8553    _ = editor.update(cx, |editor, cx| {
 8554        assert_eq!(
 8555            editor.selections.ranges(cx),
 8556            [Point::new(0, 0)..Point::new(0, 0)]
 8557        );
 8558
 8559        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8560        editor.change_selections(None, cx, |s| s.refresh());
 8561        assert_eq!(
 8562            editor.selections.ranges(cx),
 8563            [Point::new(0, 3)..Point::new(0, 3)]
 8564        );
 8565        assert!(editor.selections.pending_anchor().is_some());
 8566    });
 8567}
 8568
 8569#[gpui::test]
 8570async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8571    init_test(cx, |_| {});
 8572
 8573    let language = Arc::new(
 8574        Language::new(
 8575            LanguageConfig {
 8576                brackets: BracketPairConfig {
 8577                    pairs: vec![
 8578                        BracketPair {
 8579                            start: "{".to_string(),
 8580                            end: "}".to_string(),
 8581                            close: true,
 8582                            surround: true,
 8583                            newline: true,
 8584                        },
 8585                        BracketPair {
 8586                            start: "/* ".to_string(),
 8587                            end: " */".to_string(),
 8588                            close: true,
 8589                            surround: true,
 8590                            newline: true,
 8591                        },
 8592                    ],
 8593                    ..Default::default()
 8594                },
 8595                ..Default::default()
 8596            },
 8597            Some(tree_sitter_rust::language()),
 8598        )
 8599        .with_indents_query("")
 8600        .unwrap(),
 8601    );
 8602
 8603    let text = concat!(
 8604        "{   }\n",     //
 8605        "  x\n",       //
 8606        "  /*   */\n", //
 8607        "x\n",         //
 8608        "{{} }\n",     //
 8609    );
 8610
 8611    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8612    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8613    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8614    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8615        .await;
 8616
 8617    _ = view.update(cx, |view, cx| {
 8618        view.change_selections(None, cx, |s| {
 8619            s.select_display_ranges([
 8620                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8621                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8622                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8623            ])
 8624        });
 8625        view.newline(&Newline, cx);
 8626
 8627        assert_eq!(
 8628            view.buffer().read(cx).read(cx).text(),
 8629            concat!(
 8630                "{ \n",    // Suppress rustfmt
 8631                "\n",      //
 8632                "}\n",     //
 8633                "  x\n",   //
 8634                "  /* \n", //
 8635                "  \n",    //
 8636                "  */\n",  //
 8637                "x\n",     //
 8638                "{{} \n",  //
 8639                "}\n",     //
 8640            )
 8641        );
 8642    });
 8643}
 8644
 8645#[gpui::test]
 8646fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8647    init_test(cx, |_| {});
 8648
 8649    let editor = cx.add_window(|cx| {
 8650        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8651        build_editor(buffer.clone(), cx)
 8652    });
 8653
 8654    _ = editor.update(cx, |editor, cx| {
 8655        struct Type1;
 8656        struct Type2;
 8657
 8658        let buffer = editor.buffer.read(cx).snapshot(cx);
 8659
 8660        let anchor_range =
 8661            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8662
 8663        editor.highlight_background::<Type1>(
 8664            &[
 8665                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8666                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8667                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8668                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8669            ],
 8670            |_| Hsla::red(),
 8671            cx,
 8672        );
 8673        editor.highlight_background::<Type2>(
 8674            &[
 8675                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8676                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8677                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8678                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8679            ],
 8680            |_| Hsla::green(),
 8681            cx,
 8682        );
 8683
 8684        let snapshot = editor.snapshot(cx);
 8685        let mut highlighted_ranges = editor.background_highlights_in_range(
 8686            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8687            &snapshot,
 8688            cx.theme().colors(),
 8689        );
 8690        // Enforce a consistent ordering based on color without relying on the ordering of the
 8691        // highlight's `TypeId` which is non-executor.
 8692        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8693        assert_eq!(
 8694            highlighted_ranges,
 8695            &[
 8696                (
 8697                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8698                    Hsla::red(),
 8699                ),
 8700                (
 8701                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8702                    Hsla::red(),
 8703                ),
 8704                (
 8705                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8706                    Hsla::green(),
 8707                ),
 8708                (
 8709                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8710                    Hsla::green(),
 8711                ),
 8712            ]
 8713        );
 8714        assert_eq!(
 8715            editor.background_highlights_in_range(
 8716                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8717                &snapshot,
 8718                cx.theme().colors(),
 8719            ),
 8720            &[(
 8721                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8722                Hsla::red(),
 8723            )]
 8724        );
 8725    });
 8726}
 8727
 8728#[gpui::test]
 8729async fn test_following(cx: &mut gpui::TestAppContext) {
 8730    init_test(cx, |_| {});
 8731
 8732    let fs = FakeFs::new(cx.executor());
 8733    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8734
 8735    let buffer = project.update(cx, |project, cx| {
 8736        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8737        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8738    });
 8739    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8740    let follower = cx.update(|cx| {
 8741        cx.open_window(
 8742            WindowOptions {
 8743                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8744                    gpui::Point::new(px(0.), px(0.)),
 8745                    gpui::Point::new(px(10.), px(80.)),
 8746                ))),
 8747                ..Default::default()
 8748            },
 8749            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8750        )
 8751        .unwrap()
 8752    });
 8753
 8754    let is_still_following = Rc::new(RefCell::new(true));
 8755    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8756    let pending_update = Rc::new(RefCell::new(None));
 8757    _ = follower.update(cx, {
 8758        let update = pending_update.clone();
 8759        let is_still_following = is_still_following.clone();
 8760        let follower_edit_event_count = follower_edit_event_count.clone();
 8761        |_, cx| {
 8762            cx.subscribe(
 8763                &leader.root_view(cx).unwrap(),
 8764                move |_, leader, event, cx| {
 8765                    leader
 8766                        .read(cx)
 8767                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8768                },
 8769            )
 8770            .detach();
 8771
 8772            cx.subscribe(
 8773                &follower.root_view(cx).unwrap(),
 8774                move |_, _, event: &EditorEvent, _cx| {
 8775                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8776                        *is_still_following.borrow_mut() = false;
 8777                    }
 8778
 8779                    if let EditorEvent::BufferEdited = event {
 8780                        *follower_edit_event_count.borrow_mut() += 1;
 8781                    }
 8782                },
 8783            )
 8784            .detach();
 8785        }
 8786    });
 8787
 8788    // Update the selections only
 8789    _ = leader.update(cx, |leader, cx| {
 8790        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8791    });
 8792    follower
 8793        .update(cx, |follower, cx| {
 8794            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8795        })
 8796        .unwrap()
 8797        .await
 8798        .unwrap();
 8799    _ = follower.update(cx, |follower, cx| {
 8800        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8801    });
 8802    assert_eq!(*is_still_following.borrow(), true);
 8803    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8804
 8805    // Update the scroll position only
 8806    _ = leader.update(cx, |leader, cx| {
 8807        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8808    });
 8809    follower
 8810        .update(cx, |follower, cx| {
 8811            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8812        })
 8813        .unwrap()
 8814        .await
 8815        .unwrap();
 8816    assert_eq!(
 8817        follower
 8818            .update(cx, |follower, cx| follower.scroll_position(cx))
 8819            .unwrap(),
 8820        gpui::Point::new(1.5, 3.5)
 8821    );
 8822    assert_eq!(*is_still_following.borrow(), true);
 8823    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8824
 8825    // Update the selections and scroll position. The follower's scroll position is updated
 8826    // via autoscroll, not via the leader's exact scroll position.
 8827    _ = leader.update(cx, |leader, cx| {
 8828        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8829        leader.request_autoscroll(Autoscroll::newest(), cx);
 8830        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8831    });
 8832    follower
 8833        .update(cx, |follower, cx| {
 8834            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8835        })
 8836        .unwrap()
 8837        .await
 8838        .unwrap();
 8839    _ = follower.update(cx, |follower, cx| {
 8840        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8841        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8842    });
 8843    assert_eq!(*is_still_following.borrow(), true);
 8844
 8845    // Creating a pending selection that precedes another selection
 8846    _ = leader.update(cx, |leader, cx| {
 8847        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8848        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8849    });
 8850    follower
 8851        .update(cx, |follower, cx| {
 8852            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8853        })
 8854        .unwrap()
 8855        .await
 8856        .unwrap();
 8857    _ = follower.update(cx, |follower, cx| {
 8858        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8859    });
 8860    assert_eq!(*is_still_following.borrow(), true);
 8861
 8862    // Extend the pending selection so that it surrounds another selection
 8863    _ = leader.update(cx, |leader, cx| {
 8864        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8865    });
 8866    follower
 8867        .update(cx, |follower, cx| {
 8868            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8869        })
 8870        .unwrap()
 8871        .await
 8872        .unwrap();
 8873    _ = follower.update(cx, |follower, cx| {
 8874        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8875    });
 8876
 8877    // Scrolling locally breaks the follow
 8878    _ = follower.update(cx, |follower, cx| {
 8879        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8880        follower.set_scroll_anchor(
 8881            ScrollAnchor {
 8882                anchor: top_anchor,
 8883                offset: gpui::Point::new(0.0, 0.5),
 8884            },
 8885            cx,
 8886        );
 8887    });
 8888    assert_eq!(*is_still_following.borrow(), false);
 8889}
 8890
 8891#[gpui::test]
 8892async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8893    init_test(cx, |_| {});
 8894
 8895    let fs = FakeFs::new(cx.executor());
 8896    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8897    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8898    let pane = workspace
 8899        .update(cx, |workspace, _| workspace.active_pane().clone())
 8900        .unwrap();
 8901
 8902    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8903
 8904    let leader = pane.update(cx, |_, cx| {
 8905        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8906        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8907    });
 8908
 8909    // Start following the editor when it has no excerpts.
 8910    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8911    let follower_1 = cx
 8912        .update_window(*workspace.deref(), |_, cx| {
 8913            Editor::from_state_proto(
 8914                workspace.root_view(cx).unwrap(),
 8915                ViewId {
 8916                    creator: Default::default(),
 8917                    id: 0,
 8918                },
 8919                &mut state_message,
 8920                cx,
 8921            )
 8922        })
 8923        .unwrap()
 8924        .unwrap()
 8925        .await
 8926        .unwrap();
 8927
 8928    let update_message = Rc::new(RefCell::new(None));
 8929    follower_1.update(cx, {
 8930        let update = update_message.clone();
 8931        |_, cx| {
 8932            cx.subscribe(&leader, move |_, leader, event, cx| {
 8933                leader
 8934                    .read(cx)
 8935                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8936            })
 8937            .detach();
 8938        }
 8939    });
 8940
 8941    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8942        (
 8943            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8944            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8945        )
 8946    });
 8947
 8948    // Insert some excerpts.
 8949    _ = leader.update(cx, |leader, cx| {
 8950        leader.buffer.update(cx, |multibuffer, cx| {
 8951            let excerpt_ids = multibuffer.push_excerpts(
 8952                buffer_1.clone(),
 8953                [
 8954                    ExcerptRange {
 8955                        context: 1..6,
 8956                        primary: None,
 8957                    },
 8958                    ExcerptRange {
 8959                        context: 12..15,
 8960                        primary: None,
 8961                    },
 8962                    ExcerptRange {
 8963                        context: 0..3,
 8964                        primary: None,
 8965                    },
 8966                ],
 8967                cx,
 8968            );
 8969            multibuffer.insert_excerpts_after(
 8970                excerpt_ids[0],
 8971                buffer_2.clone(),
 8972                [
 8973                    ExcerptRange {
 8974                        context: 8..12,
 8975                        primary: None,
 8976                    },
 8977                    ExcerptRange {
 8978                        context: 0..6,
 8979                        primary: None,
 8980                    },
 8981                ],
 8982                cx,
 8983            );
 8984        });
 8985    });
 8986
 8987    // Apply the update of adding the excerpts.
 8988    follower_1
 8989        .update(cx, |follower, cx| {
 8990            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8991        })
 8992        .await
 8993        .unwrap();
 8994    assert_eq!(
 8995        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8996        leader.update(cx, |editor, cx| editor.text(cx))
 8997    );
 8998    update_message.borrow_mut().take();
 8999
 9000    // Start following separately after it already has excerpts.
 9001    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9002    let follower_2 = cx
 9003        .update_window(*workspace.deref(), |_, cx| {
 9004            Editor::from_state_proto(
 9005                workspace.root_view(cx).unwrap().clone(),
 9006                ViewId {
 9007                    creator: Default::default(),
 9008                    id: 0,
 9009                },
 9010                &mut state_message,
 9011                cx,
 9012            )
 9013        })
 9014        .unwrap()
 9015        .unwrap()
 9016        .await
 9017        .unwrap();
 9018    assert_eq!(
 9019        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9020        leader.update(cx, |editor, cx| editor.text(cx))
 9021    );
 9022
 9023    // Remove some excerpts.
 9024    _ = leader.update(cx, |leader, cx| {
 9025        leader.buffer.update(cx, |multibuffer, cx| {
 9026            let excerpt_ids = multibuffer.excerpt_ids();
 9027            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9028            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9029        });
 9030    });
 9031
 9032    // Apply the update of removing the excerpts.
 9033    follower_1
 9034        .update(cx, |follower, cx| {
 9035            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9036        })
 9037        .await
 9038        .unwrap();
 9039    follower_2
 9040        .update(cx, |follower, cx| {
 9041            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9042        })
 9043        .await
 9044        .unwrap();
 9045    update_message.borrow_mut().take();
 9046    assert_eq!(
 9047        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9048        leader.update(cx, |editor, cx| editor.text(cx))
 9049    );
 9050}
 9051
 9052#[gpui::test]
 9053async fn go_to_prev_overlapping_diagnostic(
 9054    executor: BackgroundExecutor,
 9055    cx: &mut gpui::TestAppContext,
 9056) {
 9057    init_test(cx, |_| {});
 9058
 9059    let mut cx = EditorTestContext::new(cx).await;
 9060    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9061
 9062    cx.set_state(indoc! {"
 9063        ˇfn func(abc def: i32) -> u32 {
 9064        }
 9065    "});
 9066
 9067    _ = cx.update(|cx| {
 9068        _ = project.update(cx, |project, cx| {
 9069            project
 9070                .update_diagnostics(
 9071                    LanguageServerId(0),
 9072                    lsp::PublishDiagnosticsParams {
 9073                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9074                        version: None,
 9075                        diagnostics: vec![
 9076                            lsp::Diagnostic {
 9077                                range: lsp::Range::new(
 9078                                    lsp::Position::new(0, 11),
 9079                                    lsp::Position::new(0, 12),
 9080                                ),
 9081                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9082                                ..Default::default()
 9083                            },
 9084                            lsp::Diagnostic {
 9085                                range: lsp::Range::new(
 9086                                    lsp::Position::new(0, 12),
 9087                                    lsp::Position::new(0, 15),
 9088                                ),
 9089                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9090                                ..Default::default()
 9091                            },
 9092                            lsp::Diagnostic {
 9093                                range: lsp::Range::new(
 9094                                    lsp::Position::new(0, 25),
 9095                                    lsp::Position::new(0, 28),
 9096                                ),
 9097                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9098                                ..Default::default()
 9099                            },
 9100                        ],
 9101                    },
 9102                    &[],
 9103                    cx,
 9104                )
 9105                .unwrap()
 9106        });
 9107    });
 9108
 9109    executor.run_until_parked();
 9110
 9111    cx.update_editor(|editor, cx| {
 9112        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9113    });
 9114
 9115    cx.assert_editor_state(indoc! {"
 9116        fn func(abc def: i32) -> ˇu32 {
 9117        }
 9118    "});
 9119
 9120    cx.update_editor(|editor, cx| {
 9121        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9122    });
 9123
 9124    cx.assert_editor_state(indoc! {"
 9125        fn func(abc ˇdef: i32) -> u32 {
 9126        }
 9127    "});
 9128
 9129    cx.update_editor(|editor, cx| {
 9130        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9131    });
 9132
 9133    cx.assert_editor_state(indoc! {"
 9134        fn func(abcˇ def: i32) -> u32 {
 9135        }
 9136    "});
 9137
 9138    cx.update_editor(|editor, cx| {
 9139        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9140    });
 9141
 9142    cx.assert_editor_state(indoc! {"
 9143        fn func(abc def: i32) -> ˇu32 {
 9144        }
 9145    "});
 9146}
 9147
 9148#[gpui::test]
 9149async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9150    init_test(cx, |_| {});
 9151
 9152    let mut cx = EditorTestContext::new(cx).await;
 9153
 9154    let diff_base = r#"
 9155        use some::mod;
 9156
 9157        const A: u32 = 42;
 9158
 9159        fn main() {
 9160            println!("hello");
 9161
 9162            println!("world");
 9163        }
 9164        "#
 9165    .unindent();
 9166
 9167    // Edits are modified, removed, modified, added
 9168    cx.set_state(
 9169        &r#"
 9170        use some::modified;
 9171
 9172        ˇ
 9173        fn main() {
 9174            println!("hello there");
 9175
 9176            println!("around the");
 9177            println!("world");
 9178        }
 9179        "#
 9180        .unindent(),
 9181    );
 9182
 9183    cx.set_diff_base(Some(&diff_base));
 9184    executor.run_until_parked();
 9185
 9186    cx.update_editor(|editor, cx| {
 9187        //Wrap around the bottom of the buffer
 9188        for _ in 0..3 {
 9189            editor.go_to_hunk(&GoToHunk, cx);
 9190        }
 9191    });
 9192
 9193    cx.assert_editor_state(
 9194        &r#"
 9195        ˇuse some::modified;
 9196
 9197
 9198        fn main() {
 9199            println!("hello there");
 9200
 9201            println!("around the");
 9202            println!("world");
 9203        }
 9204        "#
 9205        .unindent(),
 9206    );
 9207
 9208    cx.update_editor(|editor, cx| {
 9209        //Wrap around the top of the buffer
 9210        for _ in 0..2 {
 9211            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9212        }
 9213    });
 9214
 9215    cx.assert_editor_state(
 9216        &r#"
 9217        use some::modified;
 9218
 9219
 9220        fn main() {
 9221        ˇ    println!("hello there");
 9222
 9223            println!("around the");
 9224            println!("world");
 9225        }
 9226        "#
 9227        .unindent(),
 9228    );
 9229
 9230    cx.update_editor(|editor, cx| {
 9231        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9232    });
 9233
 9234    cx.assert_editor_state(
 9235        &r#"
 9236        use some::modified;
 9237
 9238        ˇ
 9239        fn main() {
 9240            println!("hello there");
 9241
 9242            println!("around the");
 9243            println!("world");
 9244        }
 9245        "#
 9246        .unindent(),
 9247    );
 9248
 9249    cx.update_editor(|editor, cx| {
 9250        for _ in 0..3 {
 9251            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9252        }
 9253    });
 9254
 9255    cx.assert_editor_state(
 9256        &r#"
 9257        use some::modified;
 9258
 9259
 9260        fn main() {
 9261        ˇ    println!("hello there");
 9262
 9263            println!("around the");
 9264            println!("world");
 9265        }
 9266        "#
 9267        .unindent(),
 9268    );
 9269
 9270    cx.update_editor(|editor, cx| {
 9271        editor.fold(&Fold, cx);
 9272
 9273        //Make sure that the fold only gets one hunk
 9274        for _ in 0..4 {
 9275            editor.go_to_hunk(&GoToHunk, cx);
 9276        }
 9277    });
 9278
 9279    cx.assert_editor_state(
 9280        &r#"
 9281        ˇuse some::modified;
 9282
 9283
 9284        fn main() {
 9285            println!("hello there");
 9286
 9287            println!("around the");
 9288            println!("world");
 9289        }
 9290        "#
 9291        .unindent(),
 9292    );
 9293}
 9294
 9295#[test]
 9296fn test_split_words() {
 9297    fn split(text: &str) -> Vec<&str> {
 9298        split_words(text).collect()
 9299    }
 9300
 9301    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9302    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9303    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9304    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9305    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9306    assert_eq!(split("helloworld"), &["helloworld"]);
 9307
 9308    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9309}
 9310
 9311#[gpui::test]
 9312async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9313    init_test(cx, |_| {});
 9314
 9315    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9316    let mut assert = |before, after| {
 9317        let _state_context = cx.set_state(before);
 9318        cx.update_editor(|editor, cx| {
 9319            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9320        });
 9321        cx.assert_editor_state(after);
 9322    };
 9323
 9324    // Outside bracket jumps to outside of matching bracket
 9325    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9326    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9327
 9328    // Inside bracket jumps to inside of matching bracket
 9329    assert("console.log(ˇvar);", "console.log(varˇ);");
 9330    assert("console.log(varˇ);", "console.log(ˇvar);");
 9331
 9332    // When outside a bracket and inside, favor jumping to the inside bracket
 9333    assert(
 9334        "console.log('foo', [1, 2, 3]ˇ);",
 9335        "console.log(ˇ'foo', [1, 2, 3]);",
 9336    );
 9337    assert(
 9338        "console.log(ˇ'foo', [1, 2, 3]);",
 9339        "console.log('foo', [1, 2, 3]ˇ);",
 9340    );
 9341
 9342    // Bias forward if two options are equally likely
 9343    assert(
 9344        "let result = curried_fun()ˇ();",
 9345        "let result = curried_fun()()ˇ;",
 9346    );
 9347
 9348    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9349    assert(
 9350        indoc! {"
 9351            function test() {
 9352                console.log('test')ˇ
 9353            }"},
 9354        indoc! {"
 9355            function test() {
 9356                console.logˇ('test')
 9357            }"},
 9358    );
 9359}
 9360
 9361#[gpui::test]
 9362async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9363    init_test(cx, |_| {});
 9364
 9365    let fs = FakeFs::new(cx.executor());
 9366    fs.insert_tree(
 9367        "/a",
 9368        json!({
 9369            "main.rs": "fn main() { let a = 5; }",
 9370            "other.rs": "// Test file",
 9371        }),
 9372    )
 9373    .await;
 9374    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9375
 9376    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9377    language_registry.add(Arc::new(Language::new(
 9378        LanguageConfig {
 9379            name: "Rust".into(),
 9380            matcher: LanguageMatcher {
 9381                path_suffixes: vec!["rs".to_string()],
 9382                ..Default::default()
 9383            },
 9384            brackets: BracketPairConfig {
 9385                pairs: vec![BracketPair {
 9386                    start: "{".to_string(),
 9387                    end: "}".to_string(),
 9388                    close: true,
 9389                    surround: true,
 9390                    newline: true,
 9391                }],
 9392                disabled_scopes_by_bracket_ix: Vec::new(),
 9393            },
 9394            ..Default::default()
 9395        },
 9396        Some(tree_sitter_rust::language()),
 9397    )));
 9398    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9399        "Rust",
 9400        FakeLspAdapter {
 9401            capabilities: lsp::ServerCapabilities {
 9402                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9403                    first_trigger_character: "{".to_string(),
 9404                    more_trigger_character: None,
 9405                }),
 9406                ..Default::default()
 9407            },
 9408            ..Default::default()
 9409        },
 9410    );
 9411
 9412    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9413
 9414    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9415
 9416    let worktree_id = workspace
 9417        .update(cx, |workspace, cx| {
 9418            workspace.project().update(cx, |project, cx| {
 9419                project.worktrees(cx).next().unwrap().read(cx).id()
 9420            })
 9421        })
 9422        .unwrap();
 9423
 9424    let buffer = project
 9425        .update(cx, |project, cx| {
 9426            project.open_local_buffer("/a/main.rs", cx)
 9427        })
 9428        .await
 9429        .unwrap();
 9430    cx.executor().run_until_parked();
 9431    cx.executor().start_waiting();
 9432    let fake_server = fake_servers.next().await.unwrap();
 9433    let editor_handle = workspace
 9434        .update(cx, |workspace, cx| {
 9435            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9436        })
 9437        .unwrap()
 9438        .await
 9439        .unwrap()
 9440        .downcast::<Editor>()
 9441        .unwrap();
 9442
 9443    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9444        assert_eq!(
 9445            params.text_document_position.text_document.uri,
 9446            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9447        );
 9448        assert_eq!(
 9449            params.text_document_position.position,
 9450            lsp::Position::new(0, 21),
 9451        );
 9452
 9453        Ok(Some(vec![lsp::TextEdit {
 9454            new_text: "]".to_string(),
 9455            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9456        }]))
 9457    });
 9458
 9459    editor_handle.update(cx, |editor, cx| {
 9460        editor.focus(cx);
 9461        editor.change_selections(None, cx, |s| {
 9462            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9463        });
 9464        editor.handle_input("{", cx);
 9465    });
 9466
 9467    cx.executor().run_until_parked();
 9468
 9469    _ = buffer.update(cx, |buffer, _| {
 9470        assert_eq!(
 9471            buffer.text(),
 9472            "fn main() { let a = {5}; }",
 9473            "No extra braces from on type formatting should appear in the buffer"
 9474        )
 9475    });
 9476}
 9477
 9478#[gpui::test]
 9479async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9480    init_test(cx, |_| {});
 9481
 9482    let fs = FakeFs::new(cx.executor());
 9483    fs.insert_tree(
 9484        "/a",
 9485        json!({
 9486            "main.rs": "fn main() { let a = 5; }",
 9487            "other.rs": "// Test file",
 9488        }),
 9489    )
 9490    .await;
 9491
 9492    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9493
 9494    let server_restarts = Arc::new(AtomicUsize::new(0));
 9495    let closure_restarts = Arc::clone(&server_restarts);
 9496    let language_server_name = "test language server";
 9497    let language_name: Arc<str> = "Rust".into();
 9498
 9499    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9500    language_registry.add(Arc::new(Language::new(
 9501        LanguageConfig {
 9502            name: Arc::clone(&language_name),
 9503            matcher: LanguageMatcher {
 9504                path_suffixes: vec!["rs".to_string()],
 9505                ..Default::default()
 9506            },
 9507            ..Default::default()
 9508        },
 9509        Some(tree_sitter_rust::language()),
 9510    )));
 9511    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9512        "Rust",
 9513        FakeLspAdapter {
 9514            name: language_server_name,
 9515            initialization_options: Some(json!({
 9516                "testOptionValue": true
 9517            })),
 9518            initializer: Some(Box::new(move |fake_server| {
 9519                let task_restarts = Arc::clone(&closure_restarts);
 9520                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9521                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9522                    futures::future::ready(Ok(()))
 9523                });
 9524            })),
 9525            ..Default::default()
 9526        },
 9527    );
 9528
 9529    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9530    let _buffer = project
 9531        .update(cx, |project, cx| {
 9532            project.open_local_buffer("/a/main.rs", cx)
 9533        })
 9534        .await
 9535        .unwrap();
 9536    let _fake_server = fake_servers.next().await.unwrap();
 9537    update_test_language_settings(cx, |language_settings| {
 9538        language_settings.languages.insert(
 9539            Arc::clone(&language_name),
 9540            LanguageSettingsContent {
 9541                tab_size: NonZeroU32::new(8),
 9542                ..Default::default()
 9543            },
 9544        );
 9545    });
 9546    cx.executor().run_until_parked();
 9547    assert_eq!(
 9548        server_restarts.load(atomic::Ordering::Acquire),
 9549        0,
 9550        "Should not restart LSP server on an unrelated change"
 9551    );
 9552
 9553    update_test_project_settings(cx, |project_settings| {
 9554        project_settings.lsp.insert(
 9555            "Some other server name".into(),
 9556            LspSettings {
 9557                binary: None,
 9558                settings: None,
 9559                initialization_options: Some(json!({
 9560                    "some other init value": false
 9561                })),
 9562            },
 9563        );
 9564    });
 9565    cx.executor().run_until_parked();
 9566    assert_eq!(
 9567        server_restarts.load(atomic::Ordering::Acquire),
 9568        0,
 9569        "Should not restart LSP server on an unrelated LSP settings change"
 9570    );
 9571
 9572    update_test_project_settings(cx, |project_settings| {
 9573        project_settings.lsp.insert(
 9574            language_server_name.into(),
 9575            LspSettings {
 9576                binary: None,
 9577                settings: None,
 9578                initialization_options: Some(json!({
 9579                    "anotherInitValue": false
 9580                })),
 9581            },
 9582        );
 9583    });
 9584    cx.executor().run_until_parked();
 9585    assert_eq!(
 9586        server_restarts.load(atomic::Ordering::Acquire),
 9587        1,
 9588        "Should restart LSP server on a related LSP settings change"
 9589    );
 9590
 9591    update_test_project_settings(cx, |project_settings| {
 9592        project_settings.lsp.insert(
 9593            language_server_name.into(),
 9594            LspSettings {
 9595                binary: None,
 9596                settings: None,
 9597                initialization_options: Some(json!({
 9598                    "anotherInitValue": false
 9599                })),
 9600            },
 9601        );
 9602    });
 9603    cx.executor().run_until_parked();
 9604    assert_eq!(
 9605        server_restarts.load(atomic::Ordering::Acquire),
 9606        1,
 9607        "Should not restart LSP server on a related LSP settings change that is the same"
 9608    );
 9609
 9610    update_test_project_settings(cx, |project_settings| {
 9611        project_settings.lsp.insert(
 9612            language_server_name.into(),
 9613            LspSettings {
 9614                binary: None,
 9615                settings: None,
 9616                initialization_options: None,
 9617            },
 9618        );
 9619    });
 9620    cx.executor().run_until_parked();
 9621    assert_eq!(
 9622        server_restarts.load(atomic::Ordering::Acquire),
 9623        2,
 9624        "Should restart LSP server on another related LSP settings change"
 9625    );
 9626}
 9627
 9628#[gpui::test]
 9629async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9630    init_test(cx, |_| {});
 9631
 9632    let mut cx = EditorLspTestContext::new_rust(
 9633        lsp::ServerCapabilities {
 9634            completion_provider: Some(lsp::CompletionOptions {
 9635                trigger_characters: Some(vec![".".to_string()]),
 9636                resolve_provider: Some(true),
 9637                ..Default::default()
 9638            }),
 9639            ..Default::default()
 9640        },
 9641        cx,
 9642    )
 9643    .await;
 9644
 9645    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9646    cx.simulate_keystroke(".");
 9647    let completion_item = lsp::CompletionItem {
 9648        label: "some".into(),
 9649        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9650        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9651        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9652            kind: lsp::MarkupKind::Markdown,
 9653            value: "```rust\nSome(2)\n```".to_string(),
 9654        })),
 9655        deprecated: Some(false),
 9656        sort_text: Some("fffffff2".to_string()),
 9657        filter_text: Some("some".to_string()),
 9658        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9659        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9660            range: lsp::Range {
 9661                start: lsp::Position {
 9662                    line: 0,
 9663                    character: 22,
 9664                },
 9665                end: lsp::Position {
 9666                    line: 0,
 9667                    character: 22,
 9668                },
 9669            },
 9670            new_text: "Some(2)".to_string(),
 9671        })),
 9672        additional_text_edits: Some(vec![lsp::TextEdit {
 9673            range: lsp::Range {
 9674                start: lsp::Position {
 9675                    line: 0,
 9676                    character: 20,
 9677                },
 9678                end: lsp::Position {
 9679                    line: 0,
 9680                    character: 22,
 9681                },
 9682            },
 9683            new_text: "".to_string(),
 9684        }]),
 9685        ..Default::default()
 9686    };
 9687
 9688    let closure_completion_item = completion_item.clone();
 9689    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9690        let task_completion_item = closure_completion_item.clone();
 9691        async move {
 9692            Ok(Some(lsp::CompletionResponse::Array(vec![
 9693                task_completion_item,
 9694            ])))
 9695        }
 9696    });
 9697
 9698    request.next().await;
 9699
 9700    cx.condition(|editor, _| editor.context_menu_visible())
 9701        .await;
 9702    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9703        editor
 9704            .confirm_completion(&ConfirmCompletion::default(), cx)
 9705            .unwrap()
 9706    });
 9707    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9708
 9709    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9710        let task_completion_item = completion_item.clone();
 9711        async move { Ok(task_completion_item) }
 9712    })
 9713    .next()
 9714    .await
 9715    .unwrap();
 9716    apply_additional_edits.await.unwrap();
 9717    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9718}
 9719
 9720#[gpui::test]
 9721async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9722    init_test(cx, |_| {});
 9723
 9724    let mut cx = EditorLspTestContext::new(
 9725        Language::new(
 9726            LanguageConfig {
 9727                matcher: LanguageMatcher {
 9728                    path_suffixes: vec!["jsx".into()],
 9729                    ..Default::default()
 9730                },
 9731                overrides: [(
 9732                    "element".into(),
 9733                    LanguageConfigOverride {
 9734                        word_characters: Override::Set(['-'].into_iter().collect()),
 9735                        ..Default::default()
 9736                    },
 9737                )]
 9738                .into_iter()
 9739                .collect(),
 9740                ..Default::default()
 9741            },
 9742            Some(tree_sitter_typescript::language_tsx()),
 9743        )
 9744        .with_override_query("(jsx_self_closing_element) @element")
 9745        .unwrap(),
 9746        lsp::ServerCapabilities {
 9747            completion_provider: Some(lsp::CompletionOptions {
 9748                trigger_characters: Some(vec![":".to_string()]),
 9749                ..Default::default()
 9750            }),
 9751            ..Default::default()
 9752        },
 9753        cx,
 9754    )
 9755    .await;
 9756
 9757    cx.lsp
 9758        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9759            Ok(Some(lsp::CompletionResponse::Array(vec![
 9760                lsp::CompletionItem {
 9761                    label: "bg-blue".into(),
 9762                    ..Default::default()
 9763                },
 9764                lsp::CompletionItem {
 9765                    label: "bg-red".into(),
 9766                    ..Default::default()
 9767                },
 9768                lsp::CompletionItem {
 9769                    label: "bg-yellow".into(),
 9770                    ..Default::default()
 9771                },
 9772            ])))
 9773        });
 9774
 9775    cx.set_state(r#"<p class="bgˇ" />"#);
 9776
 9777    // Trigger completion when typing a dash, because the dash is an extra
 9778    // word character in the 'element' scope, which contains the cursor.
 9779    cx.simulate_keystroke("-");
 9780    cx.executor().run_until_parked();
 9781    cx.update_editor(|editor, _| {
 9782        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9783            assert_eq!(
 9784                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9785                &["bg-red", "bg-blue", "bg-yellow"]
 9786            );
 9787        } else {
 9788            panic!("expected completion menu to be open");
 9789        }
 9790    });
 9791
 9792    cx.simulate_keystroke("l");
 9793    cx.executor().run_until_parked();
 9794    cx.update_editor(|editor, _| {
 9795        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9796            assert_eq!(
 9797                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9798                &["bg-blue", "bg-yellow"]
 9799            );
 9800        } else {
 9801            panic!("expected completion menu to be open");
 9802        }
 9803    });
 9804
 9805    // When filtering completions, consider the character after the '-' to
 9806    // be the start of a subword.
 9807    cx.set_state(r#"<p class="yelˇ" />"#);
 9808    cx.simulate_keystroke("l");
 9809    cx.executor().run_until_parked();
 9810    cx.update_editor(|editor, _| {
 9811        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9812            assert_eq!(
 9813                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9814                &["bg-yellow"]
 9815            );
 9816        } else {
 9817            panic!("expected completion menu to be open");
 9818        }
 9819    });
 9820}
 9821
 9822#[gpui::test]
 9823async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9824    init_test(cx, |settings| {
 9825        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9826            FormatterList(vec![Formatter::Prettier].into()),
 9827        ))
 9828    });
 9829
 9830    let fs = FakeFs::new(cx.executor());
 9831    fs.insert_file("/file.ts", Default::default()).await;
 9832
 9833    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9834    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9835
 9836    language_registry.add(Arc::new(Language::new(
 9837        LanguageConfig {
 9838            name: "TypeScript".into(),
 9839            matcher: LanguageMatcher {
 9840                path_suffixes: vec!["ts".to_string()],
 9841                ..Default::default()
 9842            },
 9843            ..Default::default()
 9844        },
 9845        Some(tree_sitter_rust::language()),
 9846    )));
 9847    update_test_language_settings(cx, |settings| {
 9848        settings.defaults.prettier = Some(PrettierSettings {
 9849            allowed: true,
 9850            ..PrettierSettings::default()
 9851        });
 9852    });
 9853
 9854    let test_plugin = "test_plugin";
 9855    let _ = language_registry.register_fake_lsp_adapter(
 9856        "TypeScript",
 9857        FakeLspAdapter {
 9858            prettier_plugins: vec![test_plugin],
 9859            ..Default::default()
 9860        },
 9861    );
 9862
 9863    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9864    let buffer = project
 9865        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9866        .await
 9867        .unwrap();
 9868
 9869    let buffer_text = "one\ntwo\nthree\n";
 9870    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9871    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9872    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9873
 9874    editor
 9875        .update(cx, |editor, cx| {
 9876            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9877        })
 9878        .unwrap()
 9879        .await;
 9880    assert_eq!(
 9881        editor.update(cx, |editor, cx| editor.text(cx)),
 9882        buffer_text.to_string() + prettier_format_suffix,
 9883        "Test prettier formatting was not applied to the original buffer text",
 9884    );
 9885
 9886    update_test_language_settings(cx, |settings| {
 9887        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9888    });
 9889    let format = editor.update(cx, |editor, cx| {
 9890        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9891    });
 9892    format.await.unwrap();
 9893    assert_eq!(
 9894        editor.update(cx, |editor, cx| editor.text(cx)),
 9895        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9896        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9897    );
 9898}
 9899
 9900#[gpui::test]
 9901async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9902    init_test(cx, |_| {});
 9903    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9904    let base_text = indoc! {r#"struct Row;
 9905struct Row1;
 9906struct Row2;
 9907
 9908struct Row4;
 9909struct Row5;
 9910struct Row6;
 9911
 9912struct Row8;
 9913struct Row9;
 9914struct Row10;"#};
 9915
 9916    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9917    assert_hunk_revert(
 9918        indoc! {r#"struct Row;
 9919                   struct Row1;
 9920                   struct Row1.1;
 9921                   struct Row1.2;
 9922                   struct Row2;ˇ
 9923
 9924                   struct Row4;
 9925                   struct Row5;
 9926                   struct Row6;
 9927
 9928                   struct Row8;
 9929                   ˇstruct Row9;
 9930                   struct Row9.1;
 9931                   struct Row9.2;
 9932                   struct Row9.3;
 9933                   struct Row10;"#},
 9934        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9935        indoc! {r#"struct Row;
 9936                   struct Row1;
 9937                   struct Row1.1;
 9938                   struct Row1.2;
 9939                   struct Row2;ˇ
 9940
 9941                   struct Row4;
 9942                   struct Row5;
 9943                   struct Row6;
 9944
 9945                   struct Row8;
 9946                   ˇstruct Row9;
 9947                   struct Row9.1;
 9948                   struct Row9.2;
 9949                   struct Row9.3;
 9950                   struct Row10;"#},
 9951        base_text,
 9952        &mut cx,
 9953    );
 9954    // Same for selections
 9955    assert_hunk_revert(
 9956        indoc! {r#"struct Row;
 9957                   struct Row1;
 9958                   struct Row2;
 9959                   struct Row2.1;
 9960                   struct Row2.2;
 9961                   «ˇ
 9962                   struct Row4;
 9963                   struct» Row5;
 9964                   «struct Row6;
 9965                   ˇ»
 9966                   struct Row9.1;
 9967                   struct Row9.2;
 9968                   struct Row9.3;
 9969                   struct Row8;
 9970                   struct Row9;
 9971                   struct Row10;"#},
 9972        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9973        indoc! {r#"struct Row;
 9974                   struct Row1;
 9975                   struct Row2;
 9976                   struct Row2.1;
 9977                   struct Row2.2;
 9978                   «ˇ
 9979                   struct Row4;
 9980                   struct» Row5;
 9981                   «struct Row6;
 9982                   ˇ»
 9983                   struct Row9.1;
 9984                   struct Row9.2;
 9985                   struct Row9.3;
 9986                   struct Row8;
 9987                   struct Row9;
 9988                   struct Row10;"#},
 9989        base_text,
 9990        &mut cx,
 9991    );
 9992
 9993    // When carets and selections intersect the addition hunks, those are reverted.
 9994    // Adjacent carets got merged.
 9995    assert_hunk_revert(
 9996        indoc! {r#"struct Row;
 9997                   ˇ// something on the top
 9998                   struct Row1;
 9999                   struct Row2;
10000                   struct Roˇw3.1;
10001                   struct Row2.2;
10002                   struct Row2.3;ˇ
10003
10004                   struct Row4;
10005                   struct ˇRow5.1;
10006                   struct Row5.2;
10007                   struct «Rowˇ»5.3;
10008                   struct Row5;
10009                   struct Row6;
10010                   ˇ
10011                   struct Row9.1;
10012                   struct «Rowˇ»9.2;
10013                   struct «ˇRow»9.3;
10014                   struct Row8;
10015                   struct Row9;
10016                   «ˇ// something on bottom»
10017                   struct Row10;"#},
10018        vec![
10019            DiffHunkStatus::Added,
10020            DiffHunkStatus::Added,
10021            DiffHunkStatus::Added,
10022            DiffHunkStatus::Added,
10023            DiffHunkStatus::Added,
10024        ],
10025        indoc! {r#"struct Row;
10026                   ˇstruct Row1;
10027                   struct Row2;
10028                   ˇ
10029                   struct Row4;
10030                   ˇstruct Row5;
10031                   struct Row6;
10032                   ˇ
10033                   ˇstruct Row8;
10034                   struct Row9;
10035                   ˇstruct Row10;"#},
10036        base_text,
10037        &mut cx,
10038    );
10039}
10040
10041#[gpui::test]
10042async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10043    init_test(cx, |_| {});
10044    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10045    let base_text = indoc! {r#"struct Row;
10046struct Row1;
10047struct Row2;
10048
10049struct Row4;
10050struct Row5;
10051struct Row6;
10052
10053struct Row8;
10054struct Row9;
10055struct Row10;"#};
10056
10057    // Modification hunks behave the same as the addition ones.
10058    assert_hunk_revert(
10059        indoc! {r#"struct Row;
10060                   struct Row1;
10061                   struct Row33;
10062                   ˇ
10063                   struct Row4;
10064                   struct Row5;
10065                   struct Row6;
10066                   ˇ
10067                   struct Row99;
10068                   struct Row9;
10069                   struct Row10;"#},
10070        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10071        indoc! {r#"struct Row;
10072                   struct Row1;
10073                   struct Row33;
10074                   ˇ
10075                   struct Row4;
10076                   struct Row5;
10077                   struct Row6;
10078                   ˇ
10079                   struct Row99;
10080                   struct Row9;
10081                   struct Row10;"#},
10082        base_text,
10083        &mut cx,
10084    );
10085    assert_hunk_revert(
10086        indoc! {r#"struct Row;
10087                   struct Row1;
10088                   struct Row33;
10089                   «ˇ
10090                   struct Row4;
10091                   struct» Row5;
10092                   «struct Row6;
10093                   ˇ»
10094                   struct Row99;
10095                   struct Row9;
10096                   struct Row10;"#},
10097        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10098        indoc! {r#"struct Row;
10099                   struct Row1;
10100                   struct Row33;
10101                   «ˇ
10102                   struct Row4;
10103                   struct» Row5;
10104                   «struct Row6;
10105                   ˇ»
10106                   struct Row99;
10107                   struct Row9;
10108                   struct Row10;"#},
10109        base_text,
10110        &mut cx,
10111    );
10112
10113    assert_hunk_revert(
10114        indoc! {r#"ˇstruct Row1.1;
10115                   struct Row1;
10116                   «ˇstr»uct Row22;
10117
10118                   struct ˇRow44;
10119                   struct Row5;
10120                   struct «Rˇ»ow66;ˇ
10121
10122                   «struˇ»ct Row88;
10123                   struct Row9;
10124                   struct Row1011;ˇ"#},
10125        vec![
10126            DiffHunkStatus::Modified,
10127            DiffHunkStatus::Modified,
10128            DiffHunkStatus::Modified,
10129            DiffHunkStatus::Modified,
10130            DiffHunkStatus::Modified,
10131            DiffHunkStatus::Modified,
10132        ],
10133        indoc! {r#"struct Row;
10134                   ˇstruct Row1;
10135                   struct Row2;
10136                   ˇ
10137                   struct Row4;
10138                   ˇstruct Row5;
10139                   struct Row6;
10140                   ˇ
10141                   struct Row8;
10142                   ˇstruct Row9;
10143                   struct Row10;ˇ"#},
10144        base_text,
10145        &mut cx,
10146    );
10147}
10148
10149#[gpui::test]
10150async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10151    init_test(cx, |_| {});
10152    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10153    let base_text = indoc! {r#"struct Row;
10154struct Row1;
10155struct Row2;
10156
10157struct Row4;
10158struct Row5;
10159struct Row6;
10160
10161struct Row8;
10162struct Row9;
10163struct Row10;"#};
10164
10165    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10166    assert_hunk_revert(
10167        indoc! {r#"struct Row;
10168                   struct Row2;
10169
10170                   ˇstruct Row4;
10171                   struct Row5;
10172                   struct Row6;
10173                   ˇ
10174                   struct Row8;
10175                   struct Row10;"#},
10176        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10177        indoc! {r#"struct Row;
10178                   struct Row2;
10179
10180                   ˇstruct Row4;
10181                   struct Row5;
10182                   struct Row6;
10183                   ˇ
10184                   struct Row8;
10185                   struct Row10;"#},
10186        base_text,
10187        &mut cx,
10188    );
10189    assert_hunk_revert(
10190        indoc! {r#"struct Row;
10191                   struct Row2;
10192
10193                   «ˇstruct Row4;
10194                   struct» Row5;
10195                   «struct Row6;
10196                   ˇ»
10197                   struct Row8;
10198                   struct Row10;"#},
10199        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10200        indoc! {r#"struct Row;
10201                   struct Row2;
10202
10203                   «ˇstruct Row4;
10204                   struct» Row5;
10205                   «struct Row6;
10206                   ˇ»
10207                   struct Row8;
10208                   struct Row10;"#},
10209        base_text,
10210        &mut cx,
10211    );
10212
10213    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10214    assert_hunk_revert(
10215        indoc! {r#"struct Row;
10216                   ˇstruct Row2;
10217
10218                   struct Row4;
10219                   struct Row5;
10220                   struct Row6;
10221
10222                   struct Row8;ˇ
10223                   struct Row10;"#},
10224        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10225        indoc! {r#"struct Row;
10226                   struct Row1;
10227                   ˇstruct Row2;
10228
10229                   struct Row4;
10230                   struct Row5;
10231                   struct Row6;
10232
10233                   struct Row8;ˇ
10234                   struct Row9;
10235                   struct Row10;"#},
10236        base_text,
10237        &mut cx,
10238    );
10239    assert_hunk_revert(
10240        indoc! {r#"struct Row;
10241                   struct Row2«ˇ;
10242                   struct Row4;
10243                   struct» Row5;
10244                   «struct Row6;
10245
10246                   struct Row8;ˇ»
10247                   struct Row10;"#},
10248        vec![
10249            DiffHunkStatus::Removed,
10250            DiffHunkStatus::Removed,
10251            DiffHunkStatus::Removed,
10252        ],
10253        indoc! {r#"struct Row;
10254                   struct Row1;
10255                   struct Row2«ˇ;
10256
10257                   struct Row4;
10258                   struct» Row5;
10259                   «struct Row6;
10260
10261                   struct Row8;ˇ»
10262                   struct Row9;
10263                   struct Row10;"#},
10264        base_text,
10265        &mut cx,
10266    );
10267}
10268
10269#[gpui::test]
10270async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10271    init_test(cx, |_| {});
10272
10273    let cols = 4;
10274    let rows = 10;
10275    let sample_text_1 = sample_text(rows, cols, 'a');
10276    assert_eq!(
10277        sample_text_1,
10278        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10279    );
10280    let sample_text_2 = sample_text(rows, cols, 'l');
10281    assert_eq!(
10282        sample_text_2,
10283        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10284    );
10285    let sample_text_3 = sample_text(rows, cols, 'v');
10286    assert_eq!(
10287        sample_text_3,
10288        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10289    );
10290
10291    fn diff_every_buffer_row(
10292        buffer: &Model<Buffer>,
10293        sample_text: String,
10294        cols: usize,
10295        cx: &mut gpui::TestAppContext,
10296    ) {
10297        // revert first character in each row, creating one large diff hunk per buffer
10298        let is_first_char = |offset: usize| offset % cols == 0;
10299        buffer.update(cx, |buffer, cx| {
10300            buffer.set_text(
10301                sample_text
10302                    .chars()
10303                    .enumerate()
10304                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10305                    .collect::<String>(),
10306                cx,
10307            );
10308            buffer.set_diff_base(Some(sample_text), cx);
10309        });
10310        cx.executor().run_until_parked();
10311    }
10312
10313    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10314    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10315
10316    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10317    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10318
10319    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10320    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10321
10322    let multibuffer = cx.new_model(|cx| {
10323        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10324        multibuffer.push_excerpts(
10325            buffer_1.clone(),
10326            [
10327                ExcerptRange {
10328                    context: Point::new(0, 0)..Point::new(3, 0),
10329                    primary: None,
10330                },
10331                ExcerptRange {
10332                    context: Point::new(5, 0)..Point::new(7, 0),
10333                    primary: None,
10334                },
10335                ExcerptRange {
10336                    context: Point::new(9, 0)..Point::new(10, 4),
10337                    primary: None,
10338                },
10339            ],
10340            cx,
10341        );
10342        multibuffer.push_excerpts(
10343            buffer_2.clone(),
10344            [
10345                ExcerptRange {
10346                    context: Point::new(0, 0)..Point::new(3, 0),
10347                    primary: None,
10348                },
10349                ExcerptRange {
10350                    context: Point::new(5, 0)..Point::new(7, 0),
10351                    primary: None,
10352                },
10353                ExcerptRange {
10354                    context: Point::new(9, 0)..Point::new(10, 4),
10355                    primary: None,
10356                },
10357            ],
10358            cx,
10359        );
10360        multibuffer.push_excerpts(
10361            buffer_3.clone(),
10362            [
10363                ExcerptRange {
10364                    context: Point::new(0, 0)..Point::new(3, 0),
10365                    primary: None,
10366                },
10367                ExcerptRange {
10368                    context: Point::new(5, 0)..Point::new(7, 0),
10369                    primary: None,
10370                },
10371                ExcerptRange {
10372                    context: Point::new(9, 0)..Point::new(10, 4),
10373                    primary: None,
10374                },
10375            ],
10376            cx,
10377        );
10378        multibuffer
10379    });
10380
10381    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10382    editor.update(cx, |editor, cx| {
10383        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");
10384        editor.select_all(&SelectAll, cx);
10385        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10386    });
10387    cx.executor().run_until_parked();
10388    // When all ranges are selected, all buffer hunks are reverted.
10389    editor.update(cx, |editor, cx| {
10390        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");
10391    });
10392    buffer_1.update(cx, |buffer, _| {
10393        assert_eq!(buffer.text(), sample_text_1);
10394    });
10395    buffer_2.update(cx, |buffer, _| {
10396        assert_eq!(buffer.text(), sample_text_2);
10397    });
10398    buffer_3.update(cx, |buffer, _| {
10399        assert_eq!(buffer.text(), sample_text_3);
10400    });
10401
10402    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10403    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10404    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10405    editor.update(cx, |editor, cx| {
10406        editor.change_selections(None, cx, |s| {
10407            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10408        });
10409        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10410    });
10411    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10412    // but not affect buffer_2 and its related excerpts.
10413    editor.update(cx, |editor, cx| {
10414        assert_eq!(
10415            editor.text(cx),
10416            "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"
10417        );
10418    });
10419    buffer_1.update(cx, |buffer, _| {
10420        assert_eq!(buffer.text(), sample_text_1);
10421    });
10422    buffer_2.update(cx, |buffer, _| {
10423        assert_eq!(
10424            buffer.text(),
10425            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10426        );
10427    });
10428    buffer_3.update(cx, |buffer, _| {
10429        assert_eq!(
10430            buffer.text(),
10431            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10432        );
10433    });
10434}
10435
10436#[gpui::test]
10437async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10438    init_test(cx, |_| {});
10439
10440    let cols = 4;
10441    let rows = 10;
10442    let sample_text_1 = sample_text(rows, cols, 'a');
10443    assert_eq!(
10444        sample_text_1,
10445        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10446    );
10447    let sample_text_2 = sample_text(rows, cols, 'l');
10448    assert_eq!(
10449        sample_text_2,
10450        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10451    );
10452    let sample_text_3 = sample_text(rows, cols, 'v');
10453    assert_eq!(
10454        sample_text_3,
10455        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10456    );
10457
10458    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10459    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10460    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10461
10462    let multi_buffer = cx.new_model(|cx| {
10463        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10464        multibuffer.push_excerpts(
10465            buffer_1.clone(),
10466            [
10467                ExcerptRange {
10468                    context: Point::new(0, 0)..Point::new(3, 0),
10469                    primary: None,
10470                },
10471                ExcerptRange {
10472                    context: Point::new(5, 0)..Point::new(7, 0),
10473                    primary: None,
10474                },
10475                ExcerptRange {
10476                    context: Point::new(9, 0)..Point::new(10, 4),
10477                    primary: None,
10478                },
10479            ],
10480            cx,
10481        );
10482        multibuffer.push_excerpts(
10483            buffer_2.clone(),
10484            [
10485                ExcerptRange {
10486                    context: Point::new(0, 0)..Point::new(3, 0),
10487                    primary: None,
10488                },
10489                ExcerptRange {
10490                    context: Point::new(5, 0)..Point::new(7, 0),
10491                    primary: None,
10492                },
10493                ExcerptRange {
10494                    context: Point::new(9, 0)..Point::new(10, 4),
10495                    primary: None,
10496                },
10497            ],
10498            cx,
10499        );
10500        multibuffer.push_excerpts(
10501            buffer_3.clone(),
10502            [
10503                ExcerptRange {
10504                    context: Point::new(0, 0)..Point::new(3, 0),
10505                    primary: None,
10506                },
10507                ExcerptRange {
10508                    context: Point::new(5, 0)..Point::new(7, 0),
10509                    primary: None,
10510                },
10511                ExcerptRange {
10512                    context: Point::new(9, 0)..Point::new(10, 4),
10513                    primary: None,
10514                },
10515            ],
10516            cx,
10517        );
10518        multibuffer
10519    });
10520
10521    let fs = FakeFs::new(cx.executor());
10522    fs.insert_tree(
10523        "/a",
10524        json!({
10525            "main.rs": sample_text_1,
10526            "other.rs": sample_text_2,
10527            "lib.rs": sample_text_3,
10528        }),
10529    )
10530    .await;
10531    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10532    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10533    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10534    let multi_buffer_editor = cx.new_view(|cx| {
10535        Editor::new(
10536            EditorMode::Full,
10537            multi_buffer,
10538            Some(project.clone()),
10539            true,
10540            cx,
10541        )
10542    });
10543    let multibuffer_item_id = workspace
10544        .update(cx, |workspace, cx| {
10545            assert!(
10546                workspace.active_item(cx).is_none(),
10547                "active item should be None before the first item is added"
10548            );
10549            workspace.add_item_to_active_pane(
10550                Box::new(multi_buffer_editor.clone()),
10551                None,
10552                true,
10553                cx,
10554            );
10555            let active_item = workspace
10556                .active_item(cx)
10557                .expect("should have an active item after adding the multi buffer");
10558            assert!(
10559                !active_item.is_singleton(cx),
10560                "A multi buffer was expected to active after adding"
10561            );
10562            active_item.item_id()
10563        })
10564        .unwrap();
10565    cx.executor().run_until_parked();
10566
10567    multi_buffer_editor.update(cx, |editor, cx| {
10568        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10569        editor.open_excerpts(&OpenExcerpts, cx);
10570    });
10571    cx.executor().run_until_parked();
10572    let first_item_id = workspace
10573        .update(cx, |workspace, cx| {
10574            let active_item = workspace
10575                .active_item(cx)
10576                .expect("should have an active item after navigating into the 1st buffer");
10577            let first_item_id = active_item.item_id();
10578            assert_ne!(
10579                first_item_id, multibuffer_item_id,
10580                "Should navigate into the 1st buffer and activate it"
10581            );
10582            assert!(
10583                active_item.is_singleton(cx),
10584                "New active item should be a singleton buffer"
10585            );
10586            assert_eq!(
10587                active_item
10588                    .act_as::<Editor>(cx)
10589                    .expect("should have navigated into an editor for the 1st buffer")
10590                    .read(cx)
10591                    .text(cx),
10592                sample_text_1
10593            );
10594
10595            workspace
10596                .go_back(workspace.active_pane().downgrade(), cx)
10597                .detach_and_log_err(cx);
10598
10599            first_item_id
10600        })
10601        .unwrap();
10602    cx.executor().run_until_parked();
10603    workspace
10604        .update(cx, |workspace, cx| {
10605            let active_item = workspace
10606                .active_item(cx)
10607                .expect("should have an active item after navigating back");
10608            assert_eq!(
10609                active_item.item_id(),
10610                multibuffer_item_id,
10611                "Should navigate back to the multi buffer"
10612            );
10613            assert!(!active_item.is_singleton(cx));
10614        })
10615        .unwrap();
10616
10617    multi_buffer_editor.update(cx, |editor, cx| {
10618        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10619            s.select_ranges(Some(39..40))
10620        });
10621        editor.open_excerpts(&OpenExcerpts, cx);
10622    });
10623    cx.executor().run_until_parked();
10624    let second_item_id = workspace
10625        .update(cx, |workspace, cx| {
10626            let active_item = workspace
10627                .active_item(cx)
10628                .expect("should have an active item after navigating into the 2nd buffer");
10629            let second_item_id = active_item.item_id();
10630            assert_ne!(
10631                second_item_id, multibuffer_item_id,
10632                "Should navigate away from the multibuffer"
10633            );
10634            assert_ne!(
10635                second_item_id, first_item_id,
10636                "Should navigate into the 2nd buffer and activate it"
10637            );
10638            assert!(
10639                active_item.is_singleton(cx),
10640                "New active item should be a singleton buffer"
10641            );
10642            assert_eq!(
10643                active_item
10644                    .act_as::<Editor>(cx)
10645                    .expect("should have navigated into an editor")
10646                    .read(cx)
10647                    .text(cx),
10648                sample_text_2
10649            );
10650
10651            workspace
10652                .go_back(workspace.active_pane().downgrade(), cx)
10653                .detach_and_log_err(cx);
10654
10655            second_item_id
10656        })
10657        .unwrap();
10658    cx.executor().run_until_parked();
10659    workspace
10660        .update(cx, |workspace, cx| {
10661            let active_item = workspace
10662                .active_item(cx)
10663                .expect("should have an active item after navigating back from the 2nd buffer");
10664            assert_eq!(
10665                active_item.item_id(),
10666                multibuffer_item_id,
10667                "Should navigate back from the 2nd buffer to the multi buffer"
10668            );
10669            assert!(!active_item.is_singleton(cx));
10670        })
10671        .unwrap();
10672
10673    multi_buffer_editor.update(cx, |editor, cx| {
10674        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10675            s.select_ranges(Some(60..70))
10676        });
10677        editor.open_excerpts(&OpenExcerpts, cx);
10678    });
10679    cx.executor().run_until_parked();
10680    workspace
10681        .update(cx, |workspace, cx| {
10682            let active_item = workspace
10683                .active_item(cx)
10684                .expect("should have an active item after navigating into the 3rd buffer");
10685            let third_item_id = active_item.item_id();
10686            assert_ne!(
10687                third_item_id, multibuffer_item_id,
10688                "Should navigate into the 3rd buffer and activate it"
10689            );
10690            assert_ne!(third_item_id, first_item_id);
10691            assert_ne!(third_item_id, second_item_id);
10692            assert!(
10693                active_item.is_singleton(cx),
10694                "New active item should be a singleton buffer"
10695            );
10696            assert_eq!(
10697                active_item
10698                    .act_as::<Editor>(cx)
10699                    .expect("should have navigated into an editor")
10700                    .read(cx)
10701                    .text(cx),
10702                sample_text_3
10703            );
10704
10705            workspace
10706                .go_back(workspace.active_pane().downgrade(), cx)
10707                .detach_and_log_err(cx);
10708        })
10709        .unwrap();
10710    cx.executor().run_until_parked();
10711    workspace
10712        .update(cx, |workspace, cx| {
10713            let active_item = workspace
10714                .active_item(cx)
10715                .expect("should have an active item after navigating back from the 3rd buffer");
10716            assert_eq!(
10717                active_item.item_id(),
10718                multibuffer_item_id,
10719                "Should navigate back from the 3rd buffer to the multi buffer"
10720            );
10721            assert!(!active_item.is_singleton(cx));
10722        })
10723        .unwrap();
10724}
10725
10726#[gpui::test]
10727async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10728    init_test(cx, |_| {});
10729
10730    let mut cx = EditorTestContext::new(cx).await;
10731
10732    let diff_base = r#"
10733        use some::mod;
10734
10735        const A: u32 = 42;
10736
10737        fn main() {
10738            println!("hello");
10739
10740            println!("world");
10741        }
10742        "#
10743    .unindent();
10744
10745    cx.set_state(
10746        &r#"
10747        use some::modified;
10748
10749        ˇ
10750        fn main() {
10751            println!("hello there");
10752
10753            println!("around the");
10754            println!("world");
10755        }
10756        "#
10757        .unindent(),
10758    );
10759
10760    cx.set_diff_base(Some(&diff_base));
10761    executor.run_until_parked();
10762    let unexpanded_hunks = vec![
10763        (
10764            "use some::mod;\n".to_string(),
10765            DiffHunkStatus::Modified,
10766            DisplayRow(0)..DisplayRow(1),
10767        ),
10768        (
10769            "const A: u32 = 42;\n".to_string(),
10770            DiffHunkStatus::Removed,
10771            DisplayRow(2)..DisplayRow(2),
10772        ),
10773        (
10774            "    println!(\"hello\");\n".to_string(),
10775            DiffHunkStatus::Modified,
10776            DisplayRow(4)..DisplayRow(5),
10777        ),
10778        (
10779            "".to_string(),
10780            DiffHunkStatus::Added,
10781            DisplayRow(6)..DisplayRow(7),
10782        ),
10783    ];
10784    cx.update_editor(|editor, cx| {
10785        let snapshot = editor.snapshot(cx);
10786        let all_hunks = editor_hunks(editor, &snapshot, cx);
10787        assert_eq!(all_hunks, unexpanded_hunks);
10788    });
10789
10790    cx.update_editor(|editor, cx| {
10791        for _ in 0..4 {
10792            editor.go_to_hunk(&GoToHunk, cx);
10793            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10794        }
10795    });
10796    executor.run_until_parked();
10797    cx.assert_editor_state(
10798        &r#"
10799        use some::modified;
10800
10801        ˇ
10802        fn main() {
10803            println!("hello there");
10804
10805            println!("around the");
10806            println!("world");
10807        }
10808        "#
10809        .unindent(),
10810    );
10811    cx.update_editor(|editor, cx| {
10812        let snapshot = editor.snapshot(cx);
10813        let all_hunks = editor_hunks(editor, &snapshot, cx);
10814        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10815        assert_eq!(
10816            expanded_hunks_background_highlights(editor, cx),
10817            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10818            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10819        );
10820        assert_eq!(
10821            all_hunks,
10822            vec![
10823                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10824                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10825                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10826                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10827            ],
10828            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10829            (from modified and removed hunks)"
10830        );
10831        assert_eq!(
10832            all_hunks, all_expanded_hunks,
10833            "Editor hunks should not change and all be expanded"
10834        );
10835    });
10836
10837    cx.update_editor(|editor, cx| {
10838        editor.cancel(&Cancel, cx);
10839
10840        let snapshot = editor.snapshot(cx);
10841        let all_hunks = editor_hunks(editor, &snapshot, cx);
10842        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10843        assert_eq!(
10844            expanded_hunks_background_highlights(editor, cx),
10845            Vec::new(),
10846            "After cancelling in editor, no git highlights should be left"
10847        );
10848        assert_eq!(
10849            all_expanded_hunks,
10850            Vec::new(),
10851            "After cancelling in editor, no hunks should be expanded"
10852        );
10853        assert_eq!(
10854            all_hunks, unexpanded_hunks,
10855            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10856        );
10857    });
10858}
10859
10860#[gpui::test]
10861async fn test_toggled_diff_base_change(
10862    executor: BackgroundExecutor,
10863    cx: &mut gpui::TestAppContext,
10864) {
10865    init_test(cx, |_| {});
10866
10867    let mut cx = EditorTestContext::new(cx).await;
10868
10869    let diff_base = r#"
10870        use some::mod1;
10871        use some::mod2;
10872
10873        const A: u32 = 42;
10874        const B: u32 = 42;
10875        const C: u32 = 42;
10876
10877        fn main(ˇ) {
10878            println!("hello");
10879
10880            println!("world");
10881        }
10882        "#
10883    .unindent();
10884
10885    cx.set_state(
10886        &r#"
10887        use some::mod2;
10888
10889        const A: u32 = 42;
10890        const C: u32 = 42;
10891
10892        fn main(ˇ) {
10893            //println!("hello");
10894
10895            println!("world");
10896            //
10897            //
10898        }
10899        "#
10900        .unindent(),
10901    );
10902
10903    cx.set_diff_base(Some(&diff_base));
10904    executor.run_until_parked();
10905    cx.update_editor(|editor, cx| {
10906        let snapshot = editor.snapshot(cx);
10907        let all_hunks = editor_hunks(editor, &snapshot, cx);
10908        assert_eq!(
10909            all_hunks,
10910            vec![
10911                (
10912                    "use some::mod1;\n".to_string(),
10913                    DiffHunkStatus::Removed,
10914                    DisplayRow(0)..DisplayRow(0)
10915                ),
10916                (
10917                    "const B: u32 = 42;\n".to_string(),
10918                    DiffHunkStatus::Removed,
10919                    DisplayRow(3)..DisplayRow(3)
10920                ),
10921                (
10922                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10923                    DiffHunkStatus::Modified,
10924                    DisplayRow(5)..DisplayRow(7)
10925                ),
10926                (
10927                    "".to_string(),
10928                    DiffHunkStatus::Added,
10929                    DisplayRow(9)..DisplayRow(11)
10930                ),
10931            ]
10932        );
10933    });
10934
10935    cx.update_editor(|editor, cx| {
10936        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10937    });
10938    executor.run_until_parked();
10939    cx.assert_editor_state(
10940        &r#"
10941        use some::mod2;
10942
10943        const A: u32 = 42;
10944        const C: u32 = 42;
10945
10946        fn main(ˇ) {
10947            //println!("hello");
10948
10949            println!("world");
10950            //
10951            //
10952        }
10953        "#
10954        .unindent(),
10955    );
10956    cx.update_editor(|editor, cx| {
10957        let snapshot = editor.snapshot(cx);
10958        let all_hunks = editor_hunks(editor, &snapshot, cx);
10959        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10960        assert_eq!(
10961            expanded_hunks_background_highlights(editor, cx),
10962            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10963            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10964        );
10965        assert_eq!(
10966            all_hunks,
10967            vec![
10968                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10969                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10970                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10971                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10972            ],
10973            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10974            (from modified and removed hunks)"
10975        );
10976        assert_eq!(
10977            all_hunks, all_expanded_hunks,
10978            "Editor hunks should not change and all be expanded"
10979        );
10980    });
10981
10982    cx.set_diff_base(Some("new diff base!"));
10983    executor.run_until_parked();
10984
10985    cx.update_editor(|editor, cx| {
10986        let snapshot = editor.snapshot(cx);
10987        let all_hunks = editor_hunks(editor, &snapshot, cx);
10988        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10989        assert_eq!(
10990            expanded_hunks_background_highlights(editor, cx),
10991            Vec::new(),
10992            "After diff base is changed, old git highlights should be removed"
10993        );
10994        assert_eq!(
10995            all_expanded_hunks,
10996            Vec::new(),
10997            "After diff base is changed, old git hunk expansions should be removed"
10998        );
10999        assert_eq!(
11000            all_hunks,
11001            vec![(
11002                "new diff base!".to_string(),
11003                DiffHunkStatus::Modified,
11004                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
11005            )],
11006            "After diff base is changed, hunks should update"
11007        );
11008    });
11009}
11010
11011#[gpui::test]
11012async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11013    init_test(cx, |_| {});
11014
11015    let mut cx = EditorTestContext::new(cx).await;
11016
11017    let diff_base = r#"
11018        use some::mod1;
11019        use some::mod2;
11020
11021        const A: u32 = 42;
11022        const B: u32 = 42;
11023        const C: u32 = 42;
11024
11025        fn main(ˇ) {
11026            println!("hello");
11027
11028            println!("world");
11029        }
11030
11031        fn another() {
11032            println!("another");
11033        }
11034
11035        fn another2() {
11036            println!("another2");
11037        }
11038        "#
11039    .unindent();
11040
11041    cx.set_state(
11042        &r#"
11043        «use some::mod2;
11044
11045        const A: u32 = 42;
11046        const C: u32 = 42;
11047
11048        fn main() {
11049            //println!("hello");
11050
11051            println!("world");
11052            //
11053            //ˇ»
11054        }
11055
11056        fn another() {
11057            println!("another");
11058            println!("another");
11059        }
11060
11061            println!("another2");
11062        }
11063        "#
11064        .unindent(),
11065    );
11066
11067    cx.set_diff_base(Some(&diff_base));
11068    executor.run_until_parked();
11069    cx.update_editor(|editor, cx| {
11070        let snapshot = editor.snapshot(cx);
11071        let all_hunks = editor_hunks(editor, &snapshot, cx);
11072        assert_eq!(
11073            all_hunks,
11074            vec![
11075                (
11076                    "use some::mod1;\n".to_string(),
11077                    DiffHunkStatus::Removed,
11078                    DisplayRow(0)..DisplayRow(0)
11079                ),
11080                (
11081                    "const B: u32 = 42;\n".to_string(),
11082                    DiffHunkStatus::Removed,
11083                    DisplayRow(3)..DisplayRow(3)
11084                ),
11085                (
11086                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11087                    DiffHunkStatus::Modified,
11088                    DisplayRow(5)..DisplayRow(7)
11089                ),
11090                (
11091                    "".to_string(),
11092                    DiffHunkStatus::Added,
11093                    DisplayRow(9)..DisplayRow(11)
11094                ),
11095                (
11096                    "".to_string(),
11097                    DiffHunkStatus::Added,
11098                    DisplayRow(15)..DisplayRow(16)
11099                ),
11100                (
11101                    "fn another2() {\n".to_string(),
11102                    DiffHunkStatus::Removed,
11103                    DisplayRow(18)..DisplayRow(18)
11104                ),
11105            ]
11106        );
11107    });
11108
11109    cx.update_editor(|editor, cx| {
11110        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11111    });
11112    executor.run_until_parked();
11113    cx.assert_editor_state(
11114        &r#"
11115        «use some::mod2;
11116
11117        const A: u32 = 42;
11118        const C: u32 = 42;
11119
11120        fn main() {
11121            //println!("hello");
11122
11123            println!("world");
11124            //
11125            //ˇ»
11126        }
11127
11128        fn another() {
11129            println!("another");
11130            println!("another");
11131        }
11132
11133            println!("another2");
11134        }
11135        "#
11136        .unindent(),
11137    );
11138    cx.update_editor(|editor, cx| {
11139        let snapshot = editor.snapshot(cx);
11140        let all_hunks = editor_hunks(editor, &snapshot, cx);
11141        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11142        assert_eq!(
11143            expanded_hunks_background_highlights(editor, cx),
11144            vec![
11145                DisplayRow(9)..=DisplayRow(10),
11146                DisplayRow(13)..=DisplayRow(14),
11147                DisplayRow(19)..=DisplayRow(19)
11148            ]
11149        );
11150        assert_eq!(
11151            all_hunks,
11152            vec![
11153                (
11154                    "use some::mod1;\n".to_string(),
11155                    DiffHunkStatus::Removed,
11156                    DisplayRow(1)..DisplayRow(1)
11157                ),
11158                (
11159                    "const B: u32 = 42;\n".to_string(),
11160                    DiffHunkStatus::Removed,
11161                    DisplayRow(5)..DisplayRow(5)
11162                ),
11163                (
11164                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11165                    DiffHunkStatus::Modified,
11166                    DisplayRow(9)..DisplayRow(11)
11167                ),
11168                (
11169                    "".to_string(),
11170                    DiffHunkStatus::Added,
11171                    DisplayRow(13)..DisplayRow(15)
11172                ),
11173                (
11174                    "".to_string(),
11175                    DiffHunkStatus::Added,
11176                    DisplayRow(19)..DisplayRow(20)
11177                ),
11178                (
11179                    "fn another2() {\n".to_string(),
11180                    DiffHunkStatus::Removed,
11181                    DisplayRow(23)..DisplayRow(23)
11182                ),
11183            ],
11184        );
11185        assert_eq!(all_hunks, all_expanded_hunks);
11186    });
11187
11188    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11189    cx.executor().run_until_parked();
11190    cx.assert_editor_state(
11191        &r#"
11192        «use some::mod2;
11193
11194        const A: u32 = 42;
11195        const C: u32 = 42;
11196
11197        fn main() {
11198            //println!("hello");
11199
11200            println!("world");
11201            //
11202            //ˇ»
11203        }
11204
11205        fn another() {
11206            println!("another");
11207            println!("another");
11208        }
11209
11210            println!("another2");
11211        }
11212        "#
11213        .unindent(),
11214    );
11215    cx.update_editor(|editor, cx| {
11216        let snapshot = editor.snapshot(cx);
11217        let all_hunks = editor_hunks(editor, &snapshot, cx);
11218        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11219        assert_eq!(
11220            expanded_hunks_background_highlights(editor, cx),
11221            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11222            "Only one hunk is left not folded, its highlight should be visible"
11223        );
11224        assert_eq!(
11225            all_hunks,
11226            vec![
11227                (
11228                    "use some::mod1;\n".to_string(),
11229                    DiffHunkStatus::Removed,
11230                    DisplayRow(0)..DisplayRow(0)
11231                ),
11232                (
11233                    "const B: u32 = 42;\n".to_string(),
11234                    DiffHunkStatus::Removed,
11235                    DisplayRow(0)..DisplayRow(0)
11236                ),
11237                (
11238                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11239                    DiffHunkStatus::Modified,
11240                    DisplayRow(0)..DisplayRow(0)
11241                ),
11242                (
11243                    "".to_string(),
11244                    DiffHunkStatus::Added,
11245                    DisplayRow(0)..DisplayRow(1)
11246                ),
11247                (
11248                    "".to_string(),
11249                    DiffHunkStatus::Added,
11250                    DisplayRow(5)..DisplayRow(6)
11251                ),
11252                (
11253                    "fn another2() {\n".to_string(),
11254                    DiffHunkStatus::Removed,
11255                    DisplayRow(9)..DisplayRow(9)
11256                ),
11257            ],
11258            "Hunk list should still return shifted folded hunks"
11259        );
11260        assert_eq!(
11261            all_expanded_hunks,
11262            vec![
11263                (
11264                    "".to_string(),
11265                    DiffHunkStatus::Added,
11266                    DisplayRow(5)..DisplayRow(6)
11267                ),
11268                (
11269                    "fn another2() {\n".to_string(),
11270                    DiffHunkStatus::Removed,
11271                    DisplayRow(9)..DisplayRow(9)
11272                ),
11273            ],
11274            "Only non-folded hunks should be left expanded"
11275        );
11276    });
11277
11278    cx.update_editor(|editor, cx| {
11279        editor.select_all(&SelectAll, cx);
11280        editor.unfold_lines(&UnfoldLines, cx);
11281    });
11282    cx.executor().run_until_parked();
11283    cx.assert_editor_state(
11284        &r#"
11285        «use some::mod2;
11286
11287        const A: u32 = 42;
11288        const C: u32 = 42;
11289
11290        fn main() {
11291            //println!("hello");
11292
11293            println!("world");
11294            //
11295            //
11296        }
11297
11298        fn another() {
11299            println!("another");
11300            println!("another");
11301        }
11302
11303            println!("another2");
11304        }
11305        ˇ»"#
11306        .unindent(),
11307    );
11308    cx.update_editor(|editor, cx| {
11309        let snapshot = editor.snapshot(cx);
11310        let all_hunks = editor_hunks(editor, &snapshot, cx);
11311        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11312        assert_eq!(
11313            expanded_hunks_background_highlights(editor, cx),
11314            vec![
11315                DisplayRow(9)..=DisplayRow(10),
11316                DisplayRow(13)..=DisplayRow(14),
11317                DisplayRow(19)..=DisplayRow(19)
11318            ],
11319            "After unfolding, all hunk diffs should be visible again"
11320        );
11321        assert_eq!(
11322            all_hunks,
11323            vec![
11324                (
11325                    "use some::mod1;\n".to_string(),
11326                    DiffHunkStatus::Removed,
11327                    DisplayRow(1)..DisplayRow(1)
11328                ),
11329                (
11330                    "const B: u32 = 42;\n".to_string(),
11331                    DiffHunkStatus::Removed,
11332                    DisplayRow(5)..DisplayRow(5)
11333                ),
11334                (
11335                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11336                    DiffHunkStatus::Modified,
11337                    DisplayRow(9)..DisplayRow(11)
11338                ),
11339                (
11340                    "".to_string(),
11341                    DiffHunkStatus::Added,
11342                    DisplayRow(13)..DisplayRow(15)
11343                ),
11344                (
11345                    "".to_string(),
11346                    DiffHunkStatus::Added,
11347                    DisplayRow(19)..DisplayRow(20)
11348                ),
11349                (
11350                    "fn another2() {\n".to_string(),
11351                    DiffHunkStatus::Removed,
11352                    DisplayRow(23)..DisplayRow(23)
11353                ),
11354            ],
11355        );
11356        assert_eq!(all_hunks, all_expanded_hunks);
11357    });
11358}
11359
11360#[gpui::test]
11361async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11362    init_test(cx, |_| {});
11363
11364    let cols = 4;
11365    let rows = 10;
11366    let sample_text_1 = sample_text(rows, cols, 'a');
11367    assert_eq!(
11368        sample_text_1,
11369        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11370    );
11371    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11372    let sample_text_2 = sample_text(rows, cols, 'l');
11373    assert_eq!(
11374        sample_text_2,
11375        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11376    );
11377    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11378    let sample_text_3 = sample_text(rows, cols, 'v');
11379    assert_eq!(
11380        sample_text_3,
11381        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11382    );
11383    let modified_sample_text_3 =
11384        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11385    let buffer_1 = cx.new_model(|cx| {
11386        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11387        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11388        buffer
11389    });
11390    let buffer_2 = cx.new_model(|cx| {
11391        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11392        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11393        buffer
11394    });
11395    let buffer_3 = cx.new_model(|cx| {
11396        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11397        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11398        buffer
11399    });
11400
11401    let multi_buffer = cx.new_model(|cx| {
11402        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11403        multibuffer.push_excerpts(
11404            buffer_1.clone(),
11405            [
11406                ExcerptRange {
11407                    context: Point::new(0, 0)..Point::new(3, 0),
11408                    primary: None,
11409                },
11410                ExcerptRange {
11411                    context: Point::new(5, 0)..Point::new(7, 0),
11412                    primary: None,
11413                },
11414                ExcerptRange {
11415                    context: Point::new(9, 0)..Point::new(10, 4),
11416                    primary: None,
11417                },
11418            ],
11419            cx,
11420        );
11421        multibuffer.push_excerpts(
11422            buffer_2.clone(),
11423            [
11424                ExcerptRange {
11425                    context: Point::new(0, 0)..Point::new(3, 0),
11426                    primary: None,
11427                },
11428                ExcerptRange {
11429                    context: Point::new(5, 0)..Point::new(7, 0),
11430                    primary: None,
11431                },
11432                ExcerptRange {
11433                    context: Point::new(9, 0)..Point::new(10, 4),
11434                    primary: None,
11435                },
11436            ],
11437            cx,
11438        );
11439        multibuffer.push_excerpts(
11440            buffer_3.clone(),
11441            [
11442                ExcerptRange {
11443                    context: Point::new(0, 0)..Point::new(3, 0),
11444                    primary: None,
11445                },
11446                ExcerptRange {
11447                    context: Point::new(5, 0)..Point::new(7, 0),
11448                    primary: None,
11449                },
11450                ExcerptRange {
11451                    context: Point::new(9, 0)..Point::new(10, 4),
11452                    primary: None,
11453                },
11454            ],
11455            cx,
11456        );
11457        multibuffer
11458    });
11459
11460    let fs = FakeFs::new(cx.executor());
11461    fs.insert_tree(
11462        "/a",
11463        json!({
11464            "main.rs": modified_sample_text_1,
11465            "other.rs": modified_sample_text_2,
11466            "lib.rs": modified_sample_text_3,
11467        }),
11468    )
11469    .await;
11470
11471    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11472    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11473    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11474    let multi_buffer_editor = cx.new_view(|cx| {
11475        Editor::new(
11476            EditorMode::Full,
11477            multi_buffer,
11478            Some(project.clone()),
11479            true,
11480            cx,
11481        )
11482    });
11483    cx.executor().run_until_parked();
11484
11485    let expected_all_hunks = vec![
11486        (
11487            "bbbb\n".to_string(),
11488            DiffHunkStatus::Removed,
11489            DisplayRow(4)..DisplayRow(4),
11490        ),
11491        (
11492            "nnnn\n".to_string(),
11493            DiffHunkStatus::Modified,
11494            DisplayRow(21)..DisplayRow(22),
11495        ),
11496        (
11497            "".to_string(),
11498            DiffHunkStatus::Added,
11499            DisplayRow(41)..DisplayRow(42),
11500        ),
11501    ];
11502    let expected_all_hunks_shifted = vec![
11503        (
11504            "bbbb\n".to_string(),
11505            DiffHunkStatus::Removed,
11506            DisplayRow(5)..DisplayRow(5),
11507        ),
11508        (
11509            "nnnn\n".to_string(),
11510            DiffHunkStatus::Modified,
11511            DisplayRow(23)..DisplayRow(24),
11512        ),
11513        (
11514            "".to_string(),
11515            DiffHunkStatus::Added,
11516            DisplayRow(43)..DisplayRow(44),
11517        ),
11518    ];
11519
11520    multi_buffer_editor.update(cx, |editor, cx| {
11521        let snapshot = editor.snapshot(cx);
11522        let all_hunks = editor_hunks(editor, &snapshot, cx);
11523        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11524        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11525        assert_eq!(all_hunks, expected_all_hunks);
11526        assert_eq!(all_expanded_hunks, Vec::new());
11527    });
11528
11529    multi_buffer_editor.update(cx, |editor, cx| {
11530        editor.select_all(&SelectAll, cx);
11531        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11532    });
11533    cx.executor().run_until_parked();
11534    multi_buffer_editor.update(cx, |editor, cx| {
11535        let snapshot = editor.snapshot(cx);
11536        let all_hunks = editor_hunks(editor, &snapshot, cx);
11537        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11538        assert_eq!(
11539            expanded_hunks_background_highlights(editor, cx),
11540            vec![
11541                DisplayRow(23)..=DisplayRow(23),
11542                DisplayRow(43)..=DisplayRow(43)
11543            ],
11544        );
11545        assert_eq!(all_hunks, expected_all_hunks_shifted);
11546        assert_eq!(all_hunks, all_expanded_hunks);
11547    });
11548
11549    multi_buffer_editor.update(cx, |editor, cx| {
11550        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11551    });
11552    cx.executor().run_until_parked();
11553    multi_buffer_editor.update(cx, |editor, cx| {
11554        let snapshot = editor.snapshot(cx);
11555        let all_hunks = editor_hunks(editor, &snapshot, cx);
11556        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11557        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11558        assert_eq!(all_hunks, expected_all_hunks);
11559        assert_eq!(all_expanded_hunks, Vec::new());
11560    });
11561
11562    multi_buffer_editor.update(cx, |editor, cx| {
11563        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11564    });
11565    cx.executor().run_until_parked();
11566    multi_buffer_editor.update(cx, |editor, cx| {
11567        let snapshot = editor.snapshot(cx);
11568        let all_hunks = editor_hunks(editor, &snapshot, cx);
11569        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11570        assert_eq!(
11571            expanded_hunks_background_highlights(editor, cx),
11572            vec![
11573                DisplayRow(23)..=DisplayRow(23),
11574                DisplayRow(43)..=DisplayRow(43)
11575            ],
11576        );
11577        assert_eq!(all_hunks, expected_all_hunks_shifted);
11578        assert_eq!(all_hunks, all_expanded_hunks);
11579    });
11580
11581    multi_buffer_editor.update(cx, |editor, cx| {
11582        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11583    });
11584    cx.executor().run_until_parked();
11585    multi_buffer_editor.update(cx, |editor, cx| {
11586        let snapshot = editor.snapshot(cx);
11587        let all_hunks = editor_hunks(editor, &snapshot, cx);
11588        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11589        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11590        assert_eq!(all_hunks, expected_all_hunks);
11591        assert_eq!(all_expanded_hunks, Vec::new());
11592    });
11593}
11594
11595#[gpui::test]
11596async fn test_edits_around_toggled_additions(
11597    executor: BackgroundExecutor,
11598    cx: &mut gpui::TestAppContext,
11599) {
11600    init_test(cx, |_| {});
11601
11602    let mut cx = EditorTestContext::new(cx).await;
11603
11604    let diff_base = r#"
11605        use some::mod1;
11606        use some::mod2;
11607
11608        const A: u32 = 42;
11609
11610        fn main() {
11611            println!("hello");
11612
11613            println!("world");
11614        }
11615        "#
11616    .unindent();
11617    executor.run_until_parked();
11618    cx.set_state(
11619        &r#"
11620        use some::mod1;
11621        use some::mod2;
11622
11623        const A: u32 = 42;
11624        const B: u32 = 42;
11625        const C: u32 = 42;
11626        ˇ
11627
11628        fn main() {
11629            println!("hello");
11630
11631            println!("world");
11632        }
11633        "#
11634        .unindent(),
11635    );
11636
11637    cx.set_diff_base(Some(&diff_base));
11638    executor.run_until_parked();
11639    cx.update_editor(|editor, cx| {
11640        let snapshot = editor.snapshot(cx);
11641        let all_hunks = editor_hunks(editor, &snapshot, cx);
11642        assert_eq!(
11643            all_hunks,
11644            vec![(
11645                "".to_string(),
11646                DiffHunkStatus::Added,
11647                DisplayRow(4)..DisplayRow(7)
11648            )]
11649        );
11650    });
11651    cx.update_editor(|editor, cx| {
11652        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11653    });
11654    executor.run_until_parked();
11655    cx.assert_editor_state(
11656        &r#"
11657        use some::mod1;
11658        use some::mod2;
11659
11660        const A: u32 = 42;
11661        const B: u32 = 42;
11662        const C: u32 = 42;
11663        ˇ
11664
11665        fn main() {
11666            println!("hello");
11667
11668            println!("world");
11669        }
11670        "#
11671        .unindent(),
11672    );
11673    cx.update_editor(|editor, cx| {
11674        let snapshot = editor.snapshot(cx);
11675        let all_hunks = editor_hunks(editor, &snapshot, cx);
11676        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11677        assert_eq!(
11678            all_hunks,
11679            vec![(
11680                "".to_string(),
11681                DiffHunkStatus::Added,
11682                DisplayRow(4)..DisplayRow(7)
11683            )]
11684        );
11685        assert_eq!(
11686            expanded_hunks_background_highlights(editor, cx),
11687            vec![DisplayRow(4)..=DisplayRow(6)]
11688        );
11689        assert_eq!(all_hunks, all_expanded_hunks);
11690    });
11691
11692    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11693    executor.run_until_parked();
11694    cx.assert_editor_state(
11695        &r#"
11696        use some::mod1;
11697        use some::mod2;
11698
11699        const A: u32 = 42;
11700        const B: u32 = 42;
11701        const C: u32 = 42;
11702        const D: u32 = 42;
11703        ˇ
11704
11705        fn main() {
11706            println!("hello");
11707
11708            println!("world");
11709        }
11710        "#
11711        .unindent(),
11712    );
11713    cx.update_editor(|editor, cx| {
11714        let snapshot = editor.snapshot(cx);
11715        let all_hunks = editor_hunks(editor, &snapshot, cx);
11716        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11717        assert_eq!(
11718            all_hunks,
11719            vec![(
11720                "".to_string(),
11721                DiffHunkStatus::Added,
11722                DisplayRow(4)..DisplayRow(8)
11723            )]
11724        );
11725        assert_eq!(
11726            expanded_hunks_background_highlights(editor, cx),
11727            vec![DisplayRow(4)..=DisplayRow(6)],
11728            "Edited hunk should have one more line added"
11729        );
11730        assert_eq!(
11731            all_hunks, all_expanded_hunks,
11732            "Expanded hunk should also grow with the addition"
11733        );
11734    });
11735
11736    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11737    executor.run_until_parked();
11738    cx.assert_editor_state(
11739        &r#"
11740        use some::mod1;
11741        use some::mod2;
11742
11743        const A: u32 = 42;
11744        const B: u32 = 42;
11745        const C: u32 = 42;
11746        const D: u32 = 42;
11747        const E: u32 = 42;
11748        ˇ
11749
11750        fn main() {
11751            println!("hello");
11752
11753            println!("world");
11754        }
11755        "#
11756        .unindent(),
11757    );
11758    cx.update_editor(|editor, cx| {
11759        let snapshot = editor.snapshot(cx);
11760        let all_hunks = editor_hunks(editor, &snapshot, cx);
11761        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11762        assert_eq!(
11763            all_hunks,
11764            vec![(
11765                "".to_string(),
11766                DiffHunkStatus::Added,
11767                DisplayRow(4)..DisplayRow(9)
11768            )]
11769        );
11770        assert_eq!(
11771            expanded_hunks_background_highlights(editor, cx),
11772            vec![DisplayRow(4)..=DisplayRow(6)],
11773            "Edited hunk should have one more line added"
11774        );
11775        assert_eq!(all_hunks, all_expanded_hunks);
11776    });
11777
11778    cx.update_editor(|editor, cx| {
11779        editor.move_up(&MoveUp, cx);
11780        editor.delete_line(&DeleteLine, cx);
11781    });
11782    executor.run_until_parked();
11783    cx.assert_editor_state(
11784        &r#"
11785        use some::mod1;
11786        use some::mod2;
11787
11788        const A: u32 = 42;
11789        const B: u32 = 42;
11790        const C: u32 = 42;
11791        const D: u32 = 42;
11792        ˇ
11793
11794        fn main() {
11795            println!("hello");
11796
11797            println!("world");
11798        }
11799        "#
11800        .unindent(),
11801    );
11802    cx.update_editor(|editor, cx| {
11803        let snapshot = editor.snapshot(cx);
11804        let all_hunks = editor_hunks(editor, &snapshot, cx);
11805        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11806        assert_eq!(
11807            all_hunks,
11808            vec![(
11809                "".to_string(),
11810                DiffHunkStatus::Added,
11811                DisplayRow(4)..DisplayRow(8)
11812            )]
11813        );
11814        assert_eq!(
11815            expanded_hunks_background_highlights(editor, cx),
11816            vec![DisplayRow(4)..=DisplayRow(6)],
11817            "Deleting a line should shrint the hunk"
11818        );
11819        assert_eq!(
11820            all_hunks, all_expanded_hunks,
11821            "Expanded hunk should also shrink with the addition"
11822        );
11823    });
11824
11825    cx.update_editor(|editor, cx| {
11826        editor.move_up(&MoveUp, cx);
11827        editor.delete_line(&DeleteLine, cx);
11828        editor.move_up(&MoveUp, cx);
11829        editor.delete_line(&DeleteLine, cx);
11830        editor.move_up(&MoveUp, cx);
11831        editor.delete_line(&DeleteLine, cx);
11832    });
11833    executor.run_until_parked();
11834    cx.assert_editor_state(
11835        &r#"
11836        use some::mod1;
11837        use some::mod2;
11838
11839        const A: u32 = 42;
11840        ˇ
11841
11842        fn main() {
11843            println!("hello");
11844
11845            println!("world");
11846        }
11847        "#
11848        .unindent(),
11849    );
11850    cx.update_editor(|editor, cx| {
11851        let snapshot = editor.snapshot(cx);
11852        let all_hunks = editor_hunks(editor, &snapshot, cx);
11853        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11854        assert_eq!(
11855            all_hunks,
11856            vec![(
11857                "".to_string(),
11858                DiffHunkStatus::Added,
11859                DisplayRow(5)..DisplayRow(6)
11860            )]
11861        );
11862        assert_eq!(
11863            expanded_hunks_background_highlights(editor, cx),
11864            vec![DisplayRow(5)..=DisplayRow(5)]
11865        );
11866        assert_eq!(all_hunks, all_expanded_hunks);
11867    });
11868
11869    cx.update_editor(|editor, cx| {
11870        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11871        editor.delete_line(&DeleteLine, cx);
11872    });
11873    executor.run_until_parked();
11874    cx.assert_editor_state(
11875        &r#"
11876        ˇ
11877
11878        fn main() {
11879            println!("hello");
11880
11881            println!("world");
11882        }
11883        "#
11884        .unindent(),
11885    );
11886    cx.update_editor(|editor, cx| {
11887        let snapshot = editor.snapshot(cx);
11888        let all_hunks = editor_hunks(editor, &snapshot, cx);
11889        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11890        assert_eq!(
11891            all_hunks,
11892            vec![
11893                (
11894                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11895                    DiffHunkStatus::Removed,
11896                    DisplayRow(0)..DisplayRow(0)
11897                ),
11898                (
11899                    "const A: u32 = 42;\n".to_string(),
11900                    DiffHunkStatus::Removed,
11901                    DisplayRow(2)..DisplayRow(2)
11902                )
11903            ]
11904        );
11905        assert_eq!(
11906            expanded_hunks_background_highlights(editor, cx),
11907            Vec::new(),
11908            "Should close all stale expanded addition hunks"
11909        );
11910        assert_eq!(
11911            all_expanded_hunks,
11912            vec![(
11913                "const A: u32 = 42;\n".to_string(),
11914                DiffHunkStatus::Removed,
11915                DisplayRow(2)..DisplayRow(2)
11916            )],
11917            "Should open hunks that were adjacent to the stale addition one"
11918        );
11919    });
11920}
11921
11922#[gpui::test]
11923async fn test_edits_around_toggled_deletions(
11924    executor: BackgroundExecutor,
11925    cx: &mut gpui::TestAppContext,
11926) {
11927    init_test(cx, |_| {});
11928
11929    let mut cx = EditorTestContext::new(cx).await;
11930
11931    let diff_base = r#"
11932        use some::mod1;
11933        use some::mod2;
11934
11935        const A: u32 = 42;
11936        const B: u32 = 42;
11937        const C: u32 = 42;
11938
11939
11940        fn main() {
11941            println!("hello");
11942
11943            println!("world");
11944        }
11945        "#
11946    .unindent();
11947    executor.run_until_parked();
11948    cx.set_state(
11949        &r#"
11950        use some::mod1;
11951        use some::mod2;
11952
11953        ˇconst B: u32 = 42;
11954        const C: u32 = 42;
11955
11956
11957        fn main() {
11958            println!("hello");
11959
11960            println!("world");
11961        }
11962        "#
11963        .unindent(),
11964    );
11965
11966    cx.set_diff_base(Some(&diff_base));
11967    executor.run_until_parked();
11968    cx.update_editor(|editor, cx| {
11969        let snapshot = editor.snapshot(cx);
11970        let all_hunks = editor_hunks(editor, &snapshot, cx);
11971        assert_eq!(
11972            all_hunks,
11973            vec![(
11974                "const A: u32 = 42;\n".to_string(),
11975                DiffHunkStatus::Removed,
11976                DisplayRow(3)..DisplayRow(3)
11977            )]
11978        );
11979    });
11980    cx.update_editor(|editor, cx| {
11981        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11982    });
11983    executor.run_until_parked();
11984    cx.assert_editor_state(
11985        &r#"
11986        use some::mod1;
11987        use some::mod2;
11988
11989        ˇconst B: u32 = 42;
11990        const C: u32 = 42;
11991
11992
11993        fn main() {
11994            println!("hello");
11995
11996            println!("world");
11997        }
11998        "#
11999        .unindent(),
12000    );
12001    cx.update_editor(|editor, cx| {
12002        let snapshot = editor.snapshot(cx);
12003        let all_hunks = editor_hunks(editor, &snapshot, cx);
12004        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12005        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12006        assert_eq!(
12007            all_hunks,
12008            vec![(
12009                "const A: u32 = 42;\n".to_string(),
12010                DiffHunkStatus::Removed,
12011                DisplayRow(4)..DisplayRow(4)
12012            )]
12013        );
12014        assert_eq!(all_hunks, all_expanded_hunks);
12015    });
12016
12017    cx.update_editor(|editor, cx| {
12018        editor.delete_line(&DeleteLine, cx);
12019    });
12020    executor.run_until_parked();
12021    cx.assert_editor_state(
12022        &r#"
12023        use some::mod1;
12024        use some::mod2;
12025
12026        ˇconst C: u32 = 42;
12027
12028
12029        fn main() {
12030            println!("hello");
12031
12032            println!("world");
12033        }
12034        "#
12035        .unindent(),
12036    );
12037    cx.update_editor(|editor, cx| {
12038        let snapshot = editor.snapshot(cx);
12039        let all_hunks = editor_hunks(editor, &snapshot, cx);
12040        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12041        assert_eq!(
12042            expanded_hunks_background_highlights(editor, cx),
12043            Vec::new(),
12044            "Deleted hunks do not highlight current editor's background"
12045        );
12046        assert_eq!(
12047            all_hunks,
12048            vec![(
12049                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12050                DiffHunkStatus::Removed,
12051                DisplayRow(5)..DisplayRow(5)
12052            )]
12053        );
12054        assert_eq!(all_hunks, all_expanded_hunks);
12055    });
12056
12057    cx.update_editor(|editor, cx| {
12058        editor.delete_line(&DeleteLine, cx);
12059    });
12060    executor.run_until_parked();
12061    cx.assert_editor_state(
12062        &r#"
12063        use some::mod1;
12064        use some::mod2;
12065
12066        ˇ
12067
12068        fn main() {
12069            println!("hello");
12070
12071            println!("world");
12072        }
12073        "#
12074        .unindent(),
12075    );
12076    cx.update_editor(|editor, cx| {
12077        let snapshot = editor.snapshot(cx);
12078        let all_hunks = editor_hunks(editor, &snapshot, cx);
12079        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12080        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12081        assert_eq!(
12082            all_hunks,
12083            vec![(
12084                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12085                DiffHunkStatus::Removed,
12086                DisplayRow(6)..DisplayRow(6)
12087            )]
12088        );
12089        assert_eq!(all_hunks, all_expanded_hunks);
12090    });
12091
12092    cx.update_editor(|editor, cx| {
12093        editor.handle_input("replacement", cx);
12094    });
12095    executor.run_until_parked();
12096    cx.assert_editor_state(
12097        &r#"
12098        use some::mod1;
12099        use some::mod2;
12100
12101        replacementˇ
12102
12103        fn main() {
12104            println!("hello");
12105
12106            println!("world");
12107        }
12108        "#
12109        .unindent(),
12110    );
12111    cx.update_editor(|editor, cx| {
12112        let snapshot = editor.snapshot(cx);
12113        let all_hunks = editor_hunks(editor, &snapshot, cx);
12114        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12115        assert_eq!(
12116            all_hunks,
12117            vec![(
12118                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12119                DiffHunkStatus::Modified,
12120                DisplayRow(7)..DisplayRow(8)
12121            )]
12122        );
12123        assert_eq!(
12124            expanded_hunks_background_highlights(editor, cx),
12125            vec![DisplayRow(7)..=DisplayRow(7)],
12126            "Modified expanded hunks should display additions and highlight their background"
12127        );
12128        assert_eq!(all_hunks, all_expanded_hunks);
12129    });
12130}
12131
12132#[gpui::test]
12133async fn test_edits_around_toggled_modifications(
12134    executor: BackgroundExecutor,
12135    cx: &mut gpui::TestAppContext,
12136) {
12137    init_test(cx, |_| {});
12138
12139    let mut cx = EditorTestContext::new(cx).await;
12140
12141    let diff_base = r#"
12142        use some::mod1;
12143        use some::mod2;
12144
12145        const A: u32 = 42;
12146        const B: u32 = 42;
12147        const C: u32 = 42;
12148        const D: u32 = 42;
12149
12150
12151        fn main() {
12152            println!("hello");
12153
12154            println!("world");
12155        }"#
12156    .unindent();
12157    executor.run_until_parked();
12158    cx.set_state(
12159        &r#"
12160        use some::mod1;
12161        use some::mod2;
12162
12163        const A: u32 = 42;
12164        const B: u32 = 42;
12165        const C: u32 = 43ˇ
12166        const D: u32 = 42;
12167
12168
12169        fn main() {
12170            println!("hello");
12171
12172            println!("world");
12173        }"#
12174        .unindent(),
12175    );
12176
12177    cx.set_diff_base(Some(&diff_base));
12178    executor.run_until_parked();
12179    cx.update_editor(|editor, cx| {
12180        let snapshot = editor.snapshot(cx);
12181        let all_hunks = editor_hunks(editor, &snapshot, cx);
12182        assert_eq!(
12183            all_hunks,
12184            vec![(
12185                "const C: u32 = 42;\n".to_string(),
12186                DiffHunkStatus::Modified,
12187                DisplayRow(5)..DisplayRow(6)
12188            )]
12189        );
12190    });
12191    cx.update_editor(|editor, cx| {
12192        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12193    });
12194    executor.run_until_parked();
12195    cx.assert_editor_state(
12196        &r#"
12197        use some::mod1;
12198        use some::mod2;
12199
12200        const A: u32 = 42;
12201        const B: u32 = 42;
12202        const C: u32 = 43ˇ
12203        const D: u32 = 42;
12204
12205
12206        fn main() {
12207            println!("hello");
12208
12209            println!("world");
12210        }"#
12211        .unindent(),
12212    );
12213    cx.update_editor(|editor, cx| {
12214        let snapshot = editor.snapshot(cx);
12215        let all_hunks = editor_hunks(editor, &snapshot, cx);
12216        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12217        assert_eq!(
12218            expanded_hunks_background_highlights(editor, cx),
12219            vec![DisplayRow(6)..=DisplayRow(6)],
12220        );
12221        assert_eq!(
12222            all_hunks,
12223            vec![(
12224                "const C: u32 = 42;\n".to_string(),
12225                DiffHunkStatus::Modified,
12226                DisplayRow(6)..DisplayRow(7)
12227            )]
12228        );
12229        assert_eq!(all_hunks, all_expanded_hunks);
12230    });
12231
12232    cx.update_editor(|editor, cx| {
12233        editor.handle_input("\nnew_line\n", cx);
12234    });
12235    executor.run_until_parked();
12236    cx.assert_editor_state(
12237        &r#"
12238            use some::mod1;
12239            use some::mod2;
12240
12241            const A: u32 = 42;
12242            const B: u32 = 42;
12243            const C: u32 = 43
12244            new_line
12245            ˇ
12246            const D: u32 = 42;
12247
12248
12249            fn main() {
12250                println!("hello");
12251
12252                println!("world");
12253            }"#
12254        .unindent(),
12255    );
12256    cx.update_editor(|editor, cx| {
12257        let snapshot = editor.snapshot(cx);
12258        let all_hunks = editor_hunks(editor, &snapshot, cx);
12259        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12260        assert_eq!(
12261            expanded_hunks_background_highlights(editor, cx),
12262            vec![DisplayRow(6)..=DisplayRow(6)],
12263            "Modified hunk should grow highlighted lines on more text additions"
12264        );
12265        assert_eq!(
12266            all_hunks,
12267            vec![(
12268                "const C: u32 = 42;\n".to_string(),
12269                DiffHunkStatus::Modified,
12270                DisplayRow(6)..DisplayRow(9)
12271            )]
12272        );
12273        assert_eq!(all_hunks, all_expanded_hunks);
12274    });
12275
12276    cx.update_editor(|editor, cx| {
12277        editor.move_up(&MoveUp, cx);
12278        editor.move_up(&MoveUp, cx);
12279        editor.move_up(&MoveUp, cx);
12280        editor.delete_line(&DeleteLine, cx);
12281    });
12282    executor.run_until_parked();
12283    cx.assert_editor_state(
12284        &r#"
12285            use some::mod1;
12286            use some::mod2;
12287
12288            const A: u32 = 42;
12289            ˇconst C: u32 = 43
12290            new_line
12291
12292            const D: u32 = 42;
12293
12294
12295            fn main() {
12296                println!("hello");
12297
12298                println!("world");
12299            }"#
12300        .unindent(),
12301    );
12302    cx.update_editor(|editor, cx| {
12303        let snapshot = editor.snapshot(cx);
12304        let all_hunks = editor_hunks(editor, &snapshot, cx);
12305        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12306        assert_eq!(
12307            expanded_hunks_background_highlights(editor, cx),
12308            vec![DisplayRow(6)..=DisplayRow(8)],
12309        );
12310        assert_eq!(
12311            all_hunks,
12312            vec![(
12313                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12314                DiffHunkStatus::Modified,
12315                DisplayRow(6)..DisplayRow(9)
12316            )],
12317            "Modified hunk should grow deleted lines on text deletions above"
12318        );
12319        assert_eq!(all_hunks, all_expanded_hunks);
12320    });
12321
12322    cx.update_editor(|editor, cx| {
12323        editor.move_up(&MoveUp, cx);
12324        editor.handle_input("v", cx);
12325    });
12326    executor.run_until_parked();
12327    cx.assert_editor_state(
12328        &r#"
12329            use some::mod1;
12330            use some::mod2;
12331
12332            vˇconst A: u32 = 42;
12333            const C: u32 = 43
12334            new_line
12335
12336            const D: u32 = 42;
12337
12338
12339            fn main() {
12340                println!("hello");
12341
12342                println!("world");
12343            }"#
12344        .unindent(),
12345    );
12346    cx.update_editor(|editor, cx| {
12347        let snapshot = editor.snapshot(cx);
12348        let all_hunks = editor_hunks(editor, &snapshot, cx);
12349        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12350        assert_eq!(
12351            expanded_hunks_background_highlights(editor, cx),
12352            vec![DisplayRow(6)..=DisplayRow(9)],
12353            "Modified hunk should grow deleted lines on text modifications above"
12354        );
12355        assert_eq!(
12356            all_hunks,
12357            vec![(
12358                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12359                DiffHunkStatus::Modified,
12360                DisplayRow(6)..DisplayRow(10)
12361            )]
12362        );
12363        assert_eq!(all_hunks, all_expanded_hunks);
12364    });
12365
12366    cx.update_editor(|editor, cx| {
12367        editor.move_down(&MoveDown, cx);
12368        editor.move_down(&MoveDown, cx);
12369        editor.delete_line(&DeleteLine, cx)
12370    });
12371    executor.run_until_parked();
12372    cx.assert_editor_state(
12373        &r#"
12374            use some::mod1;
12375            use some::mod2;
12376
12377            vconst A: u32 = 42;
12378            const C: u32 = 43
12379            ˇ
12380            const D: u32 = 42;
12381
12382
12383            fn main() {
12384                println!("hello");
12385
12386                println!("world");
12387            }"#
12388        .unindent(),
12389    );
12390    cx.update_editor(|editor, cx| {
12391        let snapshot = editor.snapshot(cx);
12392        let all_hunks = editor_hunks(editor, &snapshot, cx);
12393        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12394        assert_eq!(
12395            expanded_hunks_background_highlights(editor, cx),
12396            vec![DisplayRow(6)..=DisplayRow(8)],
12397            "Modified hunk should grow shrink lines on modification lines removal"
12398        );
12399        assert_eq!(
12400            all_hunks,
12401            vec![(
12402                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12403                DiffHunkStatus::Modified,
12404                DisplayRow(6)..DisplayRow(9)
12405            )]
12406        );
12407        assert_eq!(all_hunks, all_expanded_hunks);
12408    });
12409
12410    cx.update_editor(|editor, cx| {
12411        editor.move_up(&MoveUp, cx);
12412        editor.move_up(&MoveUp, cx);
12413        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12414        editor.delete_line(&DeleteLine, cx)
12415    });
12416    executor.run_until_parked();
12417    cx.assert_editor_state(
12418        &r#"
12419            use some::mod1;
12420            use some::mod2;
12421
12422            ˇ
12423
12424            fn main() {
12425                println!("hello");
12426
12427                println!("world");
12428            }"#
12429        .unindent(),
12430    );
12431    cx.update_editor(|editor, cx| {
12432        let snapshot = editor.snapshot(cx);
12433        let all_hunks = editor_hunks(editor, &snapshot, cx);
12434        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12435        assert_eq!(
12436            expanded_hunks_background_highlights(editor, cx),
12437            Vec::new(),
12438            "Modified hunk should turn into a removed one on all modified lines removal"
12439        );
12440        assert_eq!(
12441            all_hunks,
12442            vec![(
12443                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12444                    .to_string(),
12445                DiffHunkStatus::Removed,
12446                DisplayRow(7)..DisplayRow(7)
12447            )]
12448        );
12449        assert_eq!(all_hunks, all_expanded_hunks);
12450    });
12451}
12452
12453#[gpui::test]
12454async fn test_multiple_expanded_hunks_merge(
12455    executor: BackgroundExecutor,
12456    cx: &mut gpui::TestAppContext,
12457) {
12458    init_test(cx, |_| {});
12459
12460    let mut cx = EditorTestContext::new(cx).await;
12461
12462    let diff_base = r#"
12463        use some::mod1;
12464        use some::mod2;
12465
12466        const A: u32 = 42;
12467        const B: u32 = 42;
12468        const C: u32 = 42;
12469        const D: u32 = 42;
12470
12471
12472        fn main() {
12473            println!("hello");
12474
12475            println!("world");
12476        }"#
12477    .unindent();
12478    executor.run_until_parked();
12479    cx.set_state(
12480        &r#"
12481        use some::mod1;
12482        use some::mod2;
12483
12484        const A: u32 = 42;
12485        const B: u32 = 42;
12486        const C: u32 = 43ˇ
12487        const D: u32 = 42;
12488
12489
12490        fn main() {
12491            println!("hello");
12492
12493            println!("world");
12494        }"#
12495        .unindent(),
12496    );
12497
12498    cx.set_diff_base(Some(&diff_base));
12499    executor.run_until_parked();
12500    cx.update_editor(|editor, cx| {
12501        let snapshot = editor.snapshot(cx);
12502        let all_hunks = editor_hunks(editor, &snapshot, cx);
12503        assert_eq!(
12504            all_hunks,
12505            vec![(
12506                "const C: u32 = 42;\n".to_string(),
12507                DiffHunkStatus::Modified,
12508                DisplayRow(5)..DisplayRow(6)
12509            )]
12510        );
12511    });
12512    cx.update_editor(|editor, cx| {
12513        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12514    });
12515    executor.run_until_parked();
12516    cx.assert_editor_state(
12517        &r#"
12518        use some::mod1;
12519        use some::mod2;
12520
12521        const A: u32 = 42;
12522        const B: u32 = 42;
12523        const C: u32 = 43ˇ
12524        const D: u32 = 42;
12525
12526
12527        fn main() {
12528            println!("hello");
12529
12530            println!("world");
12531        }"#
12532        .unindent(),
12533    );
12534    cx.update_editor(|editor, cx| {
12535        let snapshot = editor.snapshot(cx);
12536        let all_hunks = editor_hunks(editor, &snapshot, cx);
12537        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12538        assert_eq!(
12539            expanded_hunks_background_highlights(editor, cx),
12540            vec![DisplayRow(6)..=DisplayRow(6)],
12541        );
12542        assert_eq!(
12543            all_hunks,
12544            vec![(
12545                "const C: u32 = 42;\n".to_string(),
12546                DiffHunkStatus::Modified,
12547                DisplayRow(6)..DisplayRow(7)
12548            )]
12549        );
12550        assert_eq!(all_hunks, all_expanded_hunks);
12551    });
12552
12553    cx.update_editor(|editor, cx| {
12554        editor.handle_input("\nnew_line\n", cx);
12555    });
12556    executor.run_until_parked();
12557    cx.assert_editor_state(
12558        &r#"
12559            use some::mod1;
12560            use some::mod2;
12561
12562            const A: u32 = 42;
12563            const B: u32 = 42;
12564            const C: u32 = 43
12565            new_line
12566            ˇ
12567            const D: u32 = 42;
12568
12569
12570            fn main() {
12571                println!("hello");
12572
12573                println!("world");
12574            }"#
12575        .unindent(),
12576    );
12577}
12578
12579async fn setup_indent_guides_editor(
12580    text: &str,
12581    cx: &mut gpui::TestAppContext,
12582) -> (BufferId, EditorTestContext) {
12583    init_test(cx, |_| {});
12584
12585    let mut cx = EditorTestContext::new(cx).await;
12586
12587    let buffer_id = cx.update_editor(|editor, cx| {
12588        editor.set_text(text, cx);
12589        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12590        let buffer_id = buffer_ids[0];
12591        buffer_id
12592    });
12593
12594    (buffer_id, cx)
12595}
12596
12597fn assert_indent_guides(
12598    range: Range<u32>,
12599    expected: Vec<IndentGuide>,
12600    active_indices: Option<Vec<usize>>,
12601    cx: &mut EditorTestContext,
12602) {
12603    let indent_guides = cx.update_editor(|editor, cx| {
12604        let snapshot = editor.snapshot(cx).display_snapshot;
12605        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12606            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12607            true,
12608            &snapshot,
12609            cx,
12610        );
12611
12612        indent_guides.sort_by(|a, b| {
12613            a.depth.cmp(&b.depth).then(
12614                a.start_row
12615                    .cmp(&b.start_row)
12616                    .then(a.end_row.cmp(&b.end_row)),
12617            )
12618        });
12619        indent_guides
12620    });
12621
12622    if let Some(expected) = active_indices {
12623        let active_indices = cx.update_editor(|editor, cx| {
12624            let snapshot = editor.snapshot(cx).display_snapshot;
12625            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12626        });
12627
12628        assert_eq!(
12629            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12630            expected,
12631            "Active indent guide indices do not match"
12632        );
12633    }
12634
12635    let expected: Vec<_> = expected
12636        .into_iter()
12637        .map(|guide| MultiBufferIndentGuide {
12638            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12639            buffer: guide,
12640        })
12641        .collect();
12642
12643    assert_eq!(indent_guides, expected, "Indent guides do not match");
12644}
12645
12646fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12647    IndentGuide {
12648        buffer_id,
12649        start_row,
12650        end_row,
12651        depth,
12652        tab_size: 4,
12653        settings: IndentGuideSettings {
12654            enabled: true,
12655            line_width: 1,
12656            active_line_width: 1,
12657            ..Default::default()
12658        },
12659    }
12660}
12661
12662#[gpui::test]
12663async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12664    let (buffer_id, mut cx) = setup_indent_guides_editor(
12665        &"
12666    fn main() {
12667        let a = 1;
12668    }"
12669        .unindent(),
12670        cx,
12671    )
12672    .await;
12673
12674    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12675}
12676
12677#[gpui::test]
12678async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12679    let (buffer_id, mut cx) = setup_indent_guides_editor(
12680        &"
12681    fn main() {
12682        let a = 1;
12683        let b = 2;
12684    }"
12685        .unindent(),
12686        cx,
12687    )
12688    .await;
12689
12690    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12691}
12692
12693#[gpui::test]
12694async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12695    let (buffer_id, mut cx) = setup_indent_guides_editor(
12696        &"
12697    fn main() {
12698        let a = 1;
12699        if a == 3 {
12700            let b = 2;
12701        } else {
12702            let c = 3;
12703        }
12704    }"
12705        .unindent(),
12706        cx,
12707    )
12708    .await;
12709
12710    assert_indent_guides(
12711        0..8,
12712        vec![
12713            indent_guide(buffer_id, 1, 6, 0),
12714            indent_guide(buffer_id, 3, 3, 1),
12715            indent_guide(buffer_id, 5, 5, 1),
12716        ],
12717        None,
12718        &mut cx,
12719    );
12720}
12721
12722#[gpui::test]
12723async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12724    let (buffer_id, mut cx) = setup_indent_guides_editor(
12725        &"
12726    fn main() {
12727        let a = 1;
12728            let b = 2;
12729        let c = 3;
12730    }"
12731        .unindent(),
12732        cx,
12733    )
12734    .await;
12735
12736    assert_indent_guides(
12737        0..5,
12738        vec![
12739            indent_guide(buffer_id, 1, 3, 0),
12740            indent_guide(buffer_id, 2, 2, 1),
12741        ],
12742        None,
12743        &mut cx,
12744    );
12745}
12746
12747#[gpui::test]
12748async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12749    let (buffer_id, mut cx) = setup_indent_guides_editor(
12750        &"
12751        fn main() {
12752            let a = 1;
12753
12754            let c = 3;
12755        }"
12756        .unindent(),
12757        cx,
12758    )
12759    .await;
12760
12761    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12762}
12763
12764#[gpui::test]
12765async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12766    let (buffer_id, mut cx) = setup_indent_guides_editor(
12767        &"
12768        fn main() {
12769            let a = 1;
12770
12771            let c = 3;
12772
12773            if a == 3 {
12774                let b = 2;
12775            } else {
12776                let c = 3;
12777            }
12778        }"
12779        .unindent(),
12780        cx,
12781    )
12782    .await;
12783
12784    assert_indent_guides(
12785        0..11,
12786        vec![
12787            indent_guide(buffer_id, 1, 9, 0),
12788            indent_guide(buffer_id, 6, 6, 1),
12789            indent_guide(buffer_id, 8, 8, 1),
12790        ],
12791        None,
12792        &mut cx,
12793    );
12794}
12795
12796#[gpui::test]
12797async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12798    let (buffer_id, mut cx) = setup_indent_guides_editor(
12799        &"
12800        fn main() {
12801            let a = 1;
12802
12803            let c = 3;
12804
12805            if a == 3 {
12806                let b = 2;
12807            } else {
12808                let c = 3;
12809            }
12810        }"
12811        .unindent(),
12812        cx,
12813    )
12814    .await;
12815
12816    assert_indent_guides(
12817        1..11,
12818        vec![
12819            indent_guide(buffer_id, 1, 9, 0),
12820            indent_guide(buffer_id, 6, 6, 1),
12821            indent_guide(buffer_id, 8, 8, 1),
12822        ],
12823        None,
12824        &mut cx,
12825    );
12826}
12827
12828#[gpui::test]
12829async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12830    let (buffer_id, mut cx) = setup_indent_guides_editor(
12831        &"
12832        fn main() {
12833            let a = 1;
12834
12835            let c = 3;
12836
12837            if a == 3 {
12838                let b = 2;
12839            } else {
12840                let c = 3;
12841            }
12842        }"
12843        .unindent(),
12844        cx,
12845    )
12846    .await;
12847
12848    assert_indent_guides(
12849        1..10,
12850        vec![
12851            indent_guide(buffer_id, 1, 9, 0),
12852            indent_guide(buffer_id, 6, 6, 1),
12853            indent_guide(buffer_id, 8, 8, 1),
12854        ],
12855        None,
12856        &mut cx,
12857    );
12858}
12859
12860#[gpui::test]
12861async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12862    let (buffer_id, mut cx) = setup_indent_guides_editor(
12863        &"
12864        block1
12865            block2
12866                block3
12867                    block4
12868            block2
12869        block1
12870        block1"
12871            .unindent(),
12872        cx,
12873    )
12874    .await;
12875
12876    assert_indent_guides(
12877        1..10,
12878        vec![
12879            indent_guide(buffer_id, 1, 4, 0),
12880            indent_guide(buffer_id, 2, 3, 1),
12881            indent_guide(buffer_id, 3, 3, 2),
12882        ],
12883        None,
12884        &mut cx,
12885    );
12886}
12887
12888#[gpui::test]
12889async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12890    let (buffer_id, mut cx) = setup_indent_guides_editor(
12891        &"
12892        block1
12893            block2
12894                block3
12895
12896        block1
12897        block1"
12898            .unindent(),
12899        cx,
12900    )
12901    .await;
12902
12903    assert_indent_guides(
12904        0..6,
12905        vec![
12906            indent_guide(buffer_id, 1, 2, 0),
12907            indent_guide(buffer_id, 2, 2, 1),
12908        ],
12909        None,
12910        &mut cx,
12911    );
12912}
12913
12914#[gpui::test]
12915async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12916    let (buffer_id, mut cx) = setup_indent_guides_editor(
12917        &"
12918        block1
12919
12920
12921
12922            block2
12923        "
12924        .unindent(),
12925        cx,
12926    )
12927    .await;
12928
12929    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12930}
12931
12932#[gpui::test]
12933async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12934    let (buffer_id, mut cx) = setup_indent_guides_editor(
12935        &"
12936        def a:
12937        \tb = 3
12938        \tif True:
12939        \t\tc = 4
12940        \t\td = 5
12941        \tprint(b)
12942        "
12943        .unindent(),
12944        cx,
12945    )
12946    .await;
12947
12948    assert_indent_guides(
12949        0..6,
12950        vec![
12951            indent_guide(buffer_id, 1, 6, 0),
12952            indent_guide(buffer_id, 3, 4, 1),
12953        ],
12954        None,
12955        &mut cx,
12956    );
12957}
12958
12959#[gpui::test]
12960async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12961    let (buffer_id, mut cx) = setup_indent_guides_editor(
12962        &"
12963    fn main() {
12964        let a = 1;
12965    }"
12966        .unindent(),
12967        cx,
12968    )
12969    .await;
12970
12971    cx.update_editor(|editor, cx| {
12972        editor.change_selections(None, cx, |s| {
12973            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12974        });
12975    });
12976
12977    assert_indent_guides(
12978        0..3,
12979        vec![indent_guide(buffer_id, 1, 1, 0)],
12980        Some(vec![0]),
12981        &mut cx,
12982    );
12983}
12984
12985#[gpui::test]
12986async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12987    let (buffer_id, mut cx) = setup_indent_guides_editor(
12988        &"
12989    fn main() {
12990        if 1 == 2 {
12991            let a = 1;
12992        }
12993    }"
12994        .unindent(),
12995        cx,
12996    )
12997    .await;
12998
12999    cx.update_editor(|editor, cx| {
13000        editor.change_selections(None, cx, |s| {
13001            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13002        });
13003    });
13004
13005    assert_indent_guides(
13006        0..4,
13007        vec![
13008            indent_guide(buffer_id, 1, 3, 0),
13009            indent_guide(buffer_id, 2, 2, 1),
13010        ],
13011        Some(vec![1]),
13012        &mut cx,
13013    );
13014
13015    cx.update_editor(|editor, cx| {
13016        editor.change_selections(None, cx, |s| {
13017            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13018        });
13019    });
13020
13021    assert_indent_guides(
13022        0..4,
13023        vec![
13024            indent_guide(buffer_id, 1, 3, 0),
13025            indent_guide(buffer_id, 2, 2, 1),
13026        ],
13027        Some(vec![1]),
13028        &mut cx,
13029    );
13030
13031    cx.update_editor(|editor, cx| {
13032        editor.change_selections(None, cx, |s| {
13033            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13034        });
13035    });
13036
13037    assert_indent_guides(
13038        0..4,
13039        vec![
13040            indent_guide(buffer_id, 1, 3, 0),
13041            indent_guide(buffer_id, 2, 2, 1),
13042        ],
13043        Some(vec![0]),
13044        &mut cx,
13045    );
13046}
13047
13048#[gpui::test]
13049async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13050    let (buffer_id, mut cx) = setup_indent_guides_editor(
13051        &"
13052    fn main() {
13053        let a = 1;
13054
13055        let b = 2;
13056    }"
13057        .unindent(),
13058        cx,
13059    )
13060    .await;
13061
13062    cx.update_editor(|editor, cx| {
13063        editor.change_selections(None, cx, |s| {
13064            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13065        });
13066    });
13067
13068    assert_indent_guides(
13069        0..5,
13070        vec![indent_guide(buffer_id, 1, 3, 0)],
13071        Some(vec![0]),
13072        &mut cx,
13073    );
13074}
13075
13076#[gpui::test]
13077async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13078    let (buffer_id, mut cx) = setup_indent_guides_editor(
13079        &"
13080    def m:
13081        a = 1
13082        pass"
13083            .unindent(),
13084        cx,
13085    )
13086    .await;
13087
13088    cx.update_editor(|editor, cx| {
13089        editor.change_selections(None, cx, |s| {
13090            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13091        });
13092    });
13093
13094    assert_indent_guides(
13095        0..3,
13096        vec![indent_guide(buffer_id, 1, 2, 0)],
13097        Some(vec![0]),
13098        &mut cx,
13099    );
13100}
13101
13102#[gpui::test]
13103fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13104    init_test(cx, |_| {});
13105
13106    let editor = cx.add_window(|cx| {
13107        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13108        build_editor(buffer, cx)
13109    });
13110
13111    let render_args = Arc::new(Mutex::new(None));
13112    let snapshot = editor
13113        .update(cx, |editor, cx| {
13114            let snapshot = editor.buffer().read(cx).snapshot(cx);
13115            let range =
13116                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13117
13118            struct RenderArgs {
13119                row: MultiBufferRow,
13120                folded: bool,
13121                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13122            }
13123
13124            let crease = Crease::new(
13125                range,
13126                FoldPlaceholder::test(),
13127                {
13128                    let toggle_callback = render_args.clone();
13129                    move |row, folded, callback, _cx| {
13130                        *toggle_callback.lock() = Some(RenderArgs {
13131                            row,
13132                            folded,
13133                            callback,
13134                        });
13135                        div()
13136                    }
13137                },
13138                |_row, _folded, _cx| div(),
13139            );
13140
13141            editor.insert_creases(Some(crease), cx);
13142            let snapshot = editor.snapshot(cx);
13143            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13144            snapshot
13145        })
13146        .unwrap();
13147
13148    let render_args = render_args.lock().take().unwrap();
13149    assert_eq!(render_args.row, MultiBufferRow(1));
13150    assert_eq!(render_args.folded, false);
13151    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13152
13153    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13154        .unwrap();
13155    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13156    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13157
13158    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13159        .unwrap();
13160    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13161    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13162}
13163
13164fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13165    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13166    point..point
13167}
13168
13169fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13170    let (text, ranges) = marked_text_ranges(marked_text, true);
13171    assert_eq!(view.text(cx), text);
13172    assert_eq!(
13173        view.selections.ranges(cx),
13174        ranges,
13175        "Assert selections are {}",
13176        marked_text
13177    );
13178}
13179
13180pub fn handle_signature_help_request(
13181    cx: &mut EditorLspTestContext,
13182    mocked_response: lsp::SignatureHelp,
13183) -> impl Future<Output = ()> {
13184    let mut request =
13185        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13186            let mocked_response = mocked_response.clone();
13187            async move { Ok(Some(mocked_response)) }
13188        });
13189
13190    async move {
13191        request.next().await;
13192    }
13193}
13194
13195/// Handle completion request passing a marked string specifying where the completion
13196/// should be triggered from using '|' character, what range should be replaced, and what completions
13197/// should be returned using '<' and '>' to delimit the range
13198pub fn handle_completion_request(
13199    cx: &mut EditorLspTestContext,
13200    marked_string: &str,
13201    completions: Vec<&'static str>,
13202    counter: Arc<AtomicUsize>,
13203) -> impl Future<Output = ()> {
13204    let complete_from_marker: TextRangeMarker = '|'.into();
13205    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13206    let (_, mut marked_ranges) = marked_text_ranges_by(
13207        marked_string,
13208        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13209    );
13210
13211    let complete_from_position =
13212        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13213    let replace_range =
13214        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13215
13216    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13217        let completions = completions.clone();
13218        counter.fetch_add(1, atomic::Ordering::Release);
13219        async move {
13220            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13221            assert_eq!(
13222                params.text_document_position.position,
13223                complete_from_position
13224            );
13225            Ok(Some(lsp::CompletionResponse::Array(
13226                completions
13227                    .iter()
13228                    .map(|completion_text| lsp::CompletionItem {
13229                        label: completion_text.to_string(),
13230                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13231                            range: replace_range,
13232                            new_text: completion_text.to_string(),
13233                        })),
13234                        ..Default::default()
13235                    })
13236                    .collect(),
13237            )))
13238        }
13239    });
13240
13241    async move {
13242        request.next().await;
13243    }
13244}
13245
13246fn handle_resolve_completion_request(
13247    cx: &mut EditorLspTestContext,
13248    edits: Option<Vec<(&'static str, &'static str)>>,
13249) -> impl Future<Output = ()> {
13250    let edits = edits.map(|edits| {
13251        edits
13252            .iter()
13253            .map(|(marked_string, new_text)| {
13254                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13255                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13256                lsp::TextEdit::new(replace_range, new_text.to_string())
13257            })
13258            .collect::<Vec<_>>()
13259    });
13260
13261    let mut request =
13262        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13263            let edits = edits.clone();
13264            async move {
13265                Ok(lsp::CompletionItem {
13266                    additional_text_edits: edits,
13267                    ..Default::default()
13268                })
13269            }
13270        });
13271
13272    async move {
13273        request.next().await;
13274    }
13275}
13276
13277pub(crate) fn update_test_language_settings(
13278    cx: &mut TestAppContext,
13279    f: impl Fn(&mut AllLanguageSettingsContent),
13280) {
13281    _ = cx.update(|cx| {
13282        SettingsStore::update_global(cx, |store, cx| {
13283            store.update_user_settings::<AllLanguageSettings>(cx, f);
13284        });
13285    });
13286}
13287
13288pub(crate) fn update_test_project_settings(
13289    cx: &mut TestAppContext,
13290    f: impl Fn(&mut ProjectSettings),
13291) {
13292    _ = cx.update(|cx| {
13293        SettingsStore::update_global(cx, |store, cx| {
13294            store.update_user_settings::<ProjectSettings>(cx, f);
13295        });
13296    });
13297}
13298
13299pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13300    _ = cx.update(|cx| {
13301        assets::Assets.load_test_fonts(cx);
13302        let store = SettingsStore::test(cx);
13303        cx.set_global(store);
13304        theme::init(theme::LoadThemes::JustBase, cx);
13305        release_channel::init(SemanticVersion::default(), cx);
13306        client::init_settings(cx);
13307        language::init(cx);
13308        Project::init_settings(cx);
13309        workspace::init_settings(cx);
13310        crate::init(cx);
13311    });
13312
13313    update_test_language_settings(cx, f);
13314}
13315
13316pub(crate) fn rust_lang() -> Arc<Language> {
13317    Arc::new(Language::new(
13318        LanguageConfig {
13319            name: "Rust".into(),
13320            matcher: LanguageMatcher {
13321                path_suffixes: vec!["rs".to_string()],
13322                ..Default::default()
13323            },
13324            ..Default::default()
13325        },
13326        Some(tree_sitter_rust::language()),
13327    ))
13328}
13329
13330#[track_caller]
13331fn assert_hunk_revert(
13332    not_reverted_text_with_selections: &str,
13333    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13334    expected_reverted_text_with_selections: &str,
13335    base_text: &str,
13336    cx: &mut EditorLspTestContext,
13337) {
13338    cx.set_state(not_reverted_text_with_selections);
13339    cx.update_editor(|editor, cx| {
13340        editor
13341            .buffer()
13342            .read(cx)
13343            .as_singleton()
13344            .unwrap()
13345            .update(cx, |buffer, cx| {
13346                buffer.set_diff_base(Some(base_text.into()), cx);
13347            });
13348    });
13349    cx.executor().run_until_parked();
13350
13351    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13352        let snapshot = editor.buffer().read(cx).snapshot(cx);
13353        let reverted_hunk_statuses = snapshot
13354            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13355            .map(|hunk| hunk_status(&hunk))
13356            .collect::<Vec<_>>();
13357
13358        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13359        reverted_hunk_statuses
13360    });
13361    cx.executor().run_until_parked();
13362    cx.assert_editor_state(expected_reverted_text_with_selections);
13363    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13364}