editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788            }],
 3789            Some(Autoscroll::fit()),
 3790            cx,
 3791        );
 3792        editor.change_selections(None, cx, |s| {
 3793            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3794        });
 3795        editor.move_line_down(&MoveLineDown, cx);
 3796    });
 3797}
 3798
 3799#[gpui::test]
 3800fn test_transpose(cx: &mut TestAppContext) {
 3801    init_test(cx, |_| {});
 3802
 3803    _ = cx.add_window(|cx| {
 3804        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3805        editor.set_style(EditorStyle::default(), cx);
 3806        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3807        editor.transpose(&Default::default(), cx);
 3808        assert_eq!(editor.text(cx), "bac");
 3809        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3810
 3811        editor.transpose(&Default::default(), cx);
 3812        assert_eq!(editor.text(cx), "bca");
 3813        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3814
 3815        editor.transpose(&Default::default(), cx);
 3816        assert_eq!(editor.text(cx), "bac");
 3817        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3818
 3819        editor
 3820    });
 3821
 3822    _ = cx.add_window(|cx| {
 3823        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3824        editor.set_style(EditorStyle::default(), cx);
 3825        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3826        editor.transpose(&Default::default(), cx);
 3827        assert_eq!(editor.text(cx), "acb\nde");
 3828        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3829
 3830        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3831        editor.transpose(&Default::default(), cx);
 3832        assert_eq!(editor.text(cx), "acbd\ne");
 3833        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3834
 3835        editor.transpose(&Default::default(), cx);
 3836        assert_eq!(editor.text(cx), "acbde\n");
 3837        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3838
 3839        editor.transpose(&Default::default(), cx);
 3840        assert_eq!(editor.text(cx), "acbd\ne");
 3841        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3842
 3843        editor
 3844    });
 3845
 3846    _ = cx.add_window(|cx| {
 3847        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3848        editor.set_style(EditorStyle::default(), cx);
 3849        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3850        editor.transpose(&Default::default(), cx);
 3851        assert_eq!(editor.text(cx), "bacd\ne");
 3852        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3853
 3854        editor.transpose(&Default::default(), cx);
 3855        assert_eq!(editor.text(cx), "bcade\n");
 3856        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3857
 3858        editor.transpose(&Default::default(), cx);
 3859        assert_eq!(editor.text(cx), "bcda\ne");
 3860        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3861
 3862        editor.transpose(&Default::default(), cx);
 3863        assert_eq!(editor.text(cx), "bcade\n");
 3864        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3865
 3866        editor.transpose(&Default::default(), cx);
 3867        assert_eq!(editor.text(cx), "bcaed\n");
 3868        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3869
 3870        editor
 3871    });
 3872
 3873    _ = cx.add_window(|cx| {
 3874        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3875        editor.set_style(EditorStyle::default(), cx);
 3876        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3877        editor.transpose(&Default::default(), cx);
 3878        assert_eq!(editor.text(cx), "🏀🍐✋");
 3879        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3880
 3881        editor.transpose(&Default::default(), cx);
 3882        assert_eq!(editor.text(cx), "🏀✋🍐");
 3883        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3884
 3885        editor.transpose(&Default::default(), cx);
 3886        assert_eq!(editor.text(cx), "🏀🍐✋");
 3887        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3888
 3889        editor
 3890    });
 3891}
 3892
 3893#[gpui::test]
 3894async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3895    init_test(cx, |_| {});
 3896
 3897    let mut cx = EditorTestContext::new(cx).await;
 3898
 3899    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3900    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3901    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3902
 3903    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3904    cx.set_state("two ˇfour ˇsix ˇ");
 3905    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3906    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3907
 3908    // Paste again but with only two cursors. Since the number of cursors doesn't
 3909    // match the number of slices in the clipboard, the entire clipboard text
 3910    // is pasted at each cursor.
 3911    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3912    cx.update_editor(|e, cx| {
 3913        e.handle_input("( ", cx);
 3914        e.paste(&Paste, cx);
 3915        e.handle_input(") ", cx);
 3916    });
 3917    cx.assert_editor_state(
 3918        &([
 3919            "( one✅ ",
 3920            "three ",
 3921            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3922            "three ",
 3923            "five ) ˇ",
 3924        ]
 3925        .join("\n")),
 3926    );
 3927
 3928    // Cut with three selections, one of which is full-line.
 3929    cx.set_state(indoc! {"
 3930        1«2ˇ»3
 3931        4ˇ567
 3932        «8ˇ»9"});
 3933    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3934    cx.assert_editor_state(indoc! {"
 3935        1ˇ3
 3936        ˇ9"});
 3937
 3938    // Paste with three selections, noticing how the copied selection that was full-line
 3939    // gets inserted before the second cursor.
 3940    cx.set_state(indoc! {"
 3941        1ˇ3
 3942 3943        «oˇ»ne"});
 3944    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3945    cx.assert_editor_state(indoc! {"
 3946        12ˇ3
 3947        4567
 3948 3949        8ˇne"});
 3950
 3951    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3952    cx.set_state(indoc! {"
 3953        The quick brown
 3954        fox juˇmps over
 3955        the lazy dog"});
 3956    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3957    assert_eq!(
 3958        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3959        Some("fox jumps over\n".to_owned())
 3960    );
 3961
 3962    // Paste with three selections, noticing how the copied full-line selection is inserted
 3963    // before the empty selections but replaces the selection that is non-empty.
 3964    cx.set_state(indoc! {"
 3965        Tˇhe quick brown
 3966        «foˇ»x jumps over
 3967        tˇhe lazy dog"});
 3968    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3969    cx.assert_editor_state(indoc! {"
 3970        fox jumps over
 3971        Tˇhe quick brown
 3972        fox jumps over
 3973        ˇx jumps over
 3974        fox jumps over
 3975        tˇhe lazy dog"});
 3976}
 3977
 3978#[gpui::test]
 3979async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3980    init_test(cx, |_| {});
 3981
 3982    let mut cx = EditorTestContext::new(cx).await;
 3983    let language = Arc::new(Language::new(
 3984        LanguageConfig::default(),
 3985        Some(tree_sitter_rust::language()),
 3986    ));
 3987    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3988
 3989    // Cut an indented block, without the leading whitespace.
 3990    cx.set_state(indoc! {"
 3991        const a: B = (
 3992            c(),
 3993            «d(
 3994                e,
 3995                f
 3996            )ˇ»
 3997        );
 3998    "});
 3999    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4000    cx.assert_editor_state(indoc! {"
 4001        const a: B = (
 4002            c(),
 4003            ˇ
 4004        );
 4005    "});
 4006
 4007    // Paste it at the same position.
 4008    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4009    cx.assert_editor_state(indoc! {"
 4010        const a: B = (
 4011            c(),
 4012            d(
 4013                e,
 4014                f
 4015 4016        );
 4017    "});
 4018
 4019    // Paste it at a line with a lower indent level.
 4020    cx.set_state(indoc! {"
 4021        ˇ
 4022        const a: B = (
 4023            c(),
 4024        );
 4025    "});
 4026    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4027    cx.assert_editor_state(indoc! {"
 4028        d(
 4029            e,
 4030            f
 4031 4032        const a: B = (
 4033            c(),
 4034        );
 4035    "});
 4036
 4037    // Cut an indented block, with the leading whitespace.
 4038    cx.set_state(indoc! {"
 4039        const a: B = (
 4040            c(),
 4041        «    d(
 4042                e,
 4043                f
 4044            )
 4045        ˇ»);
 4046    "});
 4047    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4048    cx.assert_editor_state(indoc! {"
 4049        const a: B = (
 4050            c(),
 4051        ˇ);
 4052    "});
 4053
 4054    // Paste it at the same position.
 4055    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4056    cx.assert_editor_state(indoc! {"
 4057        const a: B = (
 4058            c(),
 4059            d(
 4060                e,
 4061                f
 4062            )
 4063        ˇ);
 4064    "});
 4065
 4066    // Paste it at a line with a higher indent level.
 4067    cx.set_state(indoc! {"
 4068        const a: B = (
 4069            c(),
 4070            d(
 4071                e,
 4072 4073            )
 4074        );
 4075    "});
 4076    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4077    cx.assert_editor_state(indoc! {"
 4078        const a: B = (
 4079            c(),
 4080            d(
 4081                e,
 4082                f    d(
 4083                    e,
 4084                    f
 4085                )
 4086        ˇ
 4087            )
 4088        );
 4089    "});
 4090}
 4091
 4092#[gpui::test]
 4093fn test_select_all(cx: &mut TestAppContext) {
 4094    init_test(cx, |_| {});
 4095
 4096    let view = cx.add_window(|cx| {
 4097        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4098        build_editor(buffer, cx)
 4099    });
 4100    _ = view.update(cx, |view, cx| {
 4101        view.select_all(&SelectAll, cx);
 4102        assert_eq!(
 4103            view.selections.display_ranges(cx),
 4104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4105        );
 4106    });
 4107}
 4108
 4109#[gpui::test]
 4110fn test_select_line(cx: &mut TestAppContext) {
 4111    init_test(cx, |_| {});
 4112
 4113    let view = cx.add_window(|cx| {
 4114        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4115        build_editor(buffer, cx)
 4116    });
 4117    _ = view.update(cx, |view, cx| {
 4118        view.change_selections(None, cx, |s| {
 4119            s.select_display_ranges([
 4120                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4121                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4122                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4123                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4124            ])
 4125        });
 4126        view.select_line(&SelectLine, cx);
 4127        assert_eq!(
 4128            view.selections.display_ranges(cx),
 4129            vec![
 4130                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    _ = view.update(cx, |view, cx| {
 4137        view.select_line(&SelectLine, cx);
 4138        assert_eq!(
 4139            view.selections.display_ranges(cx),
 4140            vec![
 4141                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4142                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4143            ]
 4144        );
 4145    });
 4146
 4147    _ = view.update(cx, |view, cx| {
 4148        view.select_line(&SelectLine, cx);
 4149        assert_eq!(
 4150            view.selections.display_ranges(cx),
 4151            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4152        );
 4153    });
 4154}
 4155
 4156#[gpui::test]
 4157fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4158    init_test(cx, |_| {});
 4159
 4160    let view = cx.add_window(|cx| {
 4161        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4162        build_editor(buffer, cx)
 4163    });
 4164    _ = view.update(cx, |view, cx| {
 4165        view.fold_ranges(
 4166            vec![
 4167                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4168                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4169                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4170            ],
 4171            true,
 4172            cx,
 4173        );
 4174        view.change_selections(None, cx, |s| {
 4175            s.select_display_ranges([
 4176                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4177                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4178                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4179                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4180            ])
 4181        });
 4182        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4183    });
 4184
 4185    _ = view.update(cx, |view, cx| {
 4186        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4187        assert_eq!(
 4188            view.display_text(cx),
 4189            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4190        );
 4191        assert_eq!(
 4192            view.selections.display_ranges(cx),
 4193            [
 4194                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4195                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4196                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4197                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4198            ]
 4199        );
 4200    });
 4201
 4202    _ = view.update(cx, |view, cx| {
 4203        view.change_selections(None, cx, |s| {
 4204            s.select_display_ranges([
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4206            ])
 4207        });
 4208        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4209        assert_eq!(
 4210            view.display_text(cx),
 4211            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4212        );
 4213        assert_eq!(
 4214            view.selections.display_ranges(cx),
 4215            [
 4216                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4217                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4218                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4219                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4220                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4221                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4222                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4223                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4224            ]
 4225        );
 4226    });
 4227}
 4228
 4229#[gpui::test]
 4230async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    let mut cx = EditorTestContext::new(cx).await;
 4234
 4235    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4236    cx.set_state(indoc!(
 4237        r#"abc
 4238           defˇghi
 4239
 4240           jk
 4241           nlmo
 4242           "#
 4243    ));
 4244
 4245    cx.update_editor(|editor, cx| {
 4246        editor.add_selection_above(&Default::default(), cx);
 4247    });
 4248
 4249    cx.assert_editor_state(indoc!(
 4250        r#"abcˇ
 4251           defˇghi
 4252
 4253           jk
 4254           nlmo
 4255           "#
 4256    ));
 4257
 4258    cx.update_editor(|editor, cx| {
 4259        editor.add_selection_above(&Default::default(), cx);
 4260    });
 4261
 4262    cx.assert_editor_state(indoc!(
 4263        r#"abcˇ
 4264            defˇghi
 4265
 4266            jk
 4267            nlmo
 4268            "#
 4269    ));
 4270
 4271    cx.update_editor(|view, cx| {
 4272        view.add_selection_below(&Default::default(), cx);
 4273    });
 4274
 4275    cx.assert_editor_state(indoc!(
 4276        r#"abc
 4277           defˇghi
 4278
 4279           jk
 4280           nlmo
 4281           "#
 4282    ));
 4283
 4284    cx.update_editor(|view, cx| {
 4285        view.undo_selection(&Default::default(), cx);
 4286    });
 4287
 4288    cx.assert_editor_state(indoc!(
 4289        r#"abcˇ
 4290           defˇghi
 4291
 4292           jk
 4293           nlmo
 4294           "#
 4295    ));
 4296
 4297    cx.update_editor(|view, cx| {
 4298        view.redo_selection(&Default::default(), cx);
 4299    });
 4300
 4301    cx.assert_editor_state(indoc!(
 4302        r#"abc
 4303           defˇghi
 4304
 4305           jk
 4306           nlmo
 4307           "#
 4308    ));
 4309
 4310    cx.update_editor(|view, cx| {
 4311        view.add_selection_below(&Default::default(), cx);
 4312    });
 4313
 4314    cx.assert_editor_state(indoc!(
 4315        r#"abc
 4316           defˇghi
 4317
 4318           jk
 4319           nlmˇo
 4320           "#
 4321    ));
 4322
 4323    cx.update_editor(|view, cx| {
 4324        view.add_selection_below(&Default::default(), cx);
 4325    });
 4326
 4327    cx.assert_editor_state(indoc!(
 4328        r#"abc
 4329           defˇghi
 4330
 4331           jk
 4332           nlmˇo
 4333           "#
 4334    ));
 4335
 4336    // change selections
 4337    cx.set_state(indoc!(
 4338        r#"abc
 4339           def«ˇg»hi
 4340
 4341           jk
 4342           nlmo
 4343           "#
 4344    ));
 4345
 4346    cx.update_editor(|view, cx| {
 4347        view.add_selection_below(&Default::default(), cx);
 4348    });
 4349
 4350    cx.assert_editor_state(indoc!(
 4351        r#"abc
 4352           def«ˇg»hi
 4353
 4354           jk
 4355           nlm«ˇo»
 4356           "#
 4357    ));
 4358
 4359    cx.update_editor(|view, cx| {
 4360        view.add_selection_below(&Default::default(), cx);
 4361    });
 4362
 4363    cx.assert_editor_state(indoc!(
 4364        r#"abc
 4365           def«ˇg»hi
 4366
 4367           jk
 4368           nlm«ˇo»
 4369           "#
 4370    ));
 4371
 4372    cx.update_editor(|view, cx| {
 4373        view.add_selection_above(&Default::default(), cx);
 4374    });
 4375
 4376    cx.assert_editor_state(indoc!(
 4377        r#"abc
 4378           def«ˇg»hi
 4379
 4380           jk
 4381           nlmo
 4382           "#
 4383    ));
 4384
 4385    cx.update_editor(|view, cx| {
 4386        view.add_selection_above(&Default::default(), cx);
 4387    });
 4388
 4389    cx.assert_editor_state(indoc!(
 4390        r#"abc
 4391           def«ˇg»hi
 4392
 4393           jk
 4394           nlmo
 4395           "#
 4396    ));
 4397
 4398    // Change selections again
 4399    cx.set_state(indoc!(
 4400        r#"a«bc
 4401           defgˇ»hi
 4402
 4403           jk
 4404           nlmo
 4405           "#
 4406    ));
 4407
 4408    cx.update_editor(|view, cx| {
 4409        view.add_selection_below(&Default::default(), cx);
 4410    });
 4411
 4412    cx.assert_editor_state(indoc!(
 4413        r#"a«bcˇ»
 4414           d«efgˇ»hi
 4415
 4416           j«kˇ»
 4417           nlmo
 4418           "#
 4419    ));
 4420
 4421    cx.update_editor(|view, cx| {
 4422        view.add_selection_below(&Default::default(), cx);
 4423    });
 4424    cx.assert_editor_state(indoc!(
 4425        r#"a«bcˇ»
 4426           d«efgˇ»hi
 4427
 4428           j«kˇ»
 4429           n«lmoˇ»
 4430           "#
 4431    ));
 4432    cx.update_editor(|view, cx| {
 4433        view.add_selection_above(&Default::default(), cx);
 4434    });
 4435
 4436    cx.assert_editor_state(indoc!(
 4437        r#"a«bcˇ»
 4438           d«efgˇ»hi
 4439
 4440           j«kˇ»
 4441           nlmo
 4442           "#
 4443    ));
 4444
 4445    // Change selections again
 4446    cx.set_state(indoc!(
 4447        r#"abc
 4448           d«ˇefghi
 4449
 4450           jk
 4451           nlm»o
 4452           "#
 4453    ));
 4454
 4455    cx.update_editor(|view, cx| {
 4456        view.add_selection_above(&Default::default(), cx);
 4457    });
 4458
 4459    cx.assert_editor_state(indoc!(
 4460        r#"a«ˇbc»
 4461           d«ˇef»ghi
 4462
 4463           j«ˇk»
 4464           n«ˇlm»o
 4465           "#
 4466    ));
 4467
 4468    cx.update_editor(|view, cx| {
 4469        view.add_selection_below(&Default::default(), cx);
 4470    });
 4471
 4472    cx.assert_editor_state(indoc!(
 4473        r#"abc
 4474           d«ˇef»ghi
 4475
 4476           j«ˇk»
 4477           n«ˇlm»o
 4478           "#
 4479    ));
 4480}
 4481
 4482#[gpui::test]
 4483async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4484    init_test(cx, |_| {});
 4485
 4486    let mut cx = EditorTestContext::new(cx).await;
 4487    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4488
 4489    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4490        .unwrap();
 4491    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4492
 4493    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4494        .unwrap();
 4495    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4496
 4497    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4498    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4499
 4500    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4501    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4502
 4503    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4504        .unwrap();
 4505    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4506
 4507    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4508        .unwrap();
 4509    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4510}
 4511
 4512#[gpui::test]
 4513async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4514    init_test(cx, |_| {});
 4515
 4516    let mut cx = EditorTestContext::new(cx).await;
 4517    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4518
 4519    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4520        .unwrap();
 4521    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4522}
 4523
 4524#[gpui::test]
 4525async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4526    init_test(cx, |_| {});
 4527
 4528    let mut cx = EditorTestContext::new(cx).await;
 4529    cx.set_state(
 4530        r#"let foo = 2;
 4531lˇet foo = 2;
 4532let fooˇ = 2;
 4533let foo = 2;
 4534let foo = ˇ2;"#,
 4535    );
 4536
 4537    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4538        .unwrap();
 4539    cx.assert_editor_state(
 4540        r#"let foo = 2;
 4541«letˇ» foo = 2;
 4542let «fooˇ» = 2;
 4543let foo = 2;
 4544let foo = «2ˇ»;"#,
 4545    );
 4546
 4547    // noop for multiple selections with different contents
 4548    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4549        .unwrap();
 4550    cx.assert_editor_state(
 4551        r#"let foo = 2;
 4552«letˇ» foo = 2;
 4553let «fooˇ» = 2;
 4554let foo = 2;
 4555let foo = «2ˇ»;"#,
 4556    );
 4557}
 4558
 4559#[gpui::test]
 4560async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4561    init_test(cx, |_| {});
 4562
 4563    let mut cx = EditorTestContext::new_multibuffer(
 4564        cx,
 4565        [
 4566            &indoc! {
 4567                "aaa\n«bbb\nccc\n»ddd"
 4568            },
 4569            &indoc! {
 4570                "aaa\n«bbb\nccc\n»ddd"
 4571            },
 4572        ],
 4573    );
 4574
 4575    cx.assert_editor_state(indoc! {"
 4576        ˇbbb
 4577        ccc
 4578
 4579        bbb
 4580        ccc
 4581        "});
 4582    cx.dispatch_action(SelectPrevious::default());
 4583    cx.assert_editor_state(indoc! {"
 4584                «bbbˇ»
 4585                ccc
 4586
 4587                bbb
 4588                ccc
 4589                "});
 4590    cx.dispatch_action(SelectPrevious::default());
 4591    cx.assert_editor_state(indoc! {"
 4592                «bbbˇ»
 4593                ccc
 4594
 4595                «bbbˇ»
 4596                ccc
 4597                "});
 4598}
 4599
 4600#[gpui::test]
 4601async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4602    init_test(cx, |_| {});
 4603
 4604    let mut cx = EditorTestContext::new(cx).await;
 4605    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4606
 4607    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4608        .unwrap();
 4609    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4610
 4611    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4612        .unwrap();
 4613    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4614
 4615    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4616    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4617
 4618    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4619    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4620
 4621    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4622        .unwrap();
 4623    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4624
 4625    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4626        .unwrap();
 4627    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4628
 4629    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4630        .unwrap();
 4631    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4632}
 4633
 4634#[gpui::test]
 4635async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4636    init_test(cx, |_| {});
 4637
 4638    let mut cx = EditorTestContext::new(cx).await;
 4639    cx.set_state(
 4640        r#"let foo = 2;
 4641lˇet foo = 2;
 4642let fooˇ = 2;
 4643let foo = 2;
 4644let foo = ˇ2;"#,
 4645    );
 4646
 4647    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4648        .unwrap();
 4649    cx.assert_editor_state(
 4650        r#"let foo = 2;
 4651«letˇ» foo = 2;
 4652let «fooˇ» = 2;
 4653let foo = 2;
 4654let foo = «2ˇ»;"#,
 4655    );
 4656
 4657    // noop for multiple selections with different contents
 4658    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4659        .unwrap();
 4660    cx.assert_editor_state(
 4661        r#"let foo = 2;
 4662«letˇ» foo = 2;
 4663let «fooˇ» = 2;
 4664let foo = 2;
 4665let foo = «2ˇ»;"#,
 4666    );
 4667}
 4668
 4669#[gpui::test]
 4670async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4671    init_test(cx, |_| {});
 4672
 4673    let mut cx = EditorTestContext::new(cx).await;
 4674    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4675
 4676    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4677        .unwrap();
 4678    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4679
 4680    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4681        .unwrap();
 4682    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4683
 4684    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4685    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4686
 4687    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4688    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4689
 4690    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4691        .unwrap();
 4692    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4693
 4694    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4695        .unwrap();
 4696    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4697}
 4698
 4699#[gpui::test]
 4700async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4701    init_test(cx, |_| {});
 4702
 4703    let language = Arc::new(Language::new(
 4704        LanguageConfig::default(),
 4705        Some(tree_sitter_rust::language()),
 4706    ));
 4707
 4708    let text = r#"
 4709        use mod1::mod2::{mod3, mod4};
 4710
 4711        fn fn_1(param1: bool, param2: &str) {
 4712            let var1 = "text";
 4713        }
 4714    "#
 4715    .unindent();
 4716
 4717    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4718    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4719    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4720
 4721    editor
 4722        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4723        .await;
 4724
 4725    editor.update(cx, |view, cx| {
 4726        view.change_selections(None, cx, |s| {
 4727            s.select_display_ranges([
 4728                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4729                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4730                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4731            ]);
 4732        });
 4733        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4734    });
 4735    editor.update(cx, |editor, cx| {
 4736        assert_text_with_selections(
 4737            editor,
 4738            indoc! {r#"
 4739                use mod1::mod2::{mod3, «mod4ˇ»};
 4740
 4741                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4742                    let var1 = "«textˇ»";
 4743                }
 4744            "#},
 4745            cx,
 4746        );
 4747    });
 4748
 4749    editor.update(cx, |view, cx| {
 4750        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4751    });
 4752    editor.update(cx, |editor, cx| {
 4753        assert_text_with_selections(
 4754            editor,
 4755            indoc! {r#"
 4756                use mod1::mod2::«{mod3, mod4}ˇ»;
 4757
 4758                «ˇfn fn_1(param1: bool, param2: &str) {
 4759                    let var1 = "text";
 4760 4761            "#},
 4762            cx,
 4763        );
 4764    });
 4765
 4766    editor.update(cx, |view, cx| {
 4767        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4768    });
 4769    assert_eq!(
 4770        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4771        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4772    );
 4773
 4774    // Trying to expand the selected syntax node one more time has no effect.
 4775    editor.update(cx, |view, cx| {
 4776        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4777    });
 4778    assert_eq!(
 4779        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4780        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4781    );
 4782
 4783    editor.update(cx, |view, cx| {
 4784        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4785    });
 4786    editor.update(cx, |editor, cx| {
 4787        assert_text_with_selections(
 4788            editor,
 4789            indoc! {r#"
 4790                use mod1::mod2::«{mod3, mod4}ˇ»;
 4791
 4792                «ˇfn fn_1(param1: bool, param2: &str) {
 4793                    let var1 = "text";
 4794 4795            "#},
 4796            cx,
 4797        );
 4798    });
 4799
 4800    editor.update(cx, |view, cx| {
 4801        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4802    });
 4803    editor.update(cx, |editor, cx| {
 4804        assert_text_with_selections(
 4805            editor,
 4806            indoc! {r#"
 4807                use mod1::mod2::{mod3, «mod4ˇ»};
 4808
 4809                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4810                    let var1 = "«textˇ»";
 4811                }
 4812            "#},
 4813            cx,
 4814        );
 4815    });
 4816
 4817    editor.update(cx, |view, cx| {
 4818        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4819    });
 4820    editor.update(cx, |editor, cx| {
 4821        assert_text_with_selections(
 4822            editor,
 4823            indoc! {r#"
 4824                use mod1::mod2::{mod3, mo«ˇ»d4};
 4825
 4826                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4827                    let var1 = "te«ˇ»xt";
 4828                }
 4829            "#},
 4830            cx,
 4831        );
 4832    });
 4833
 4834    // Trying to shrink the selected syntax node one more time has no effect.
 4835    editor.update(cx, |view, cx| {
 4836        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4837    });
 4838    editor.update(cx, |editor, cx| {
 4839        assert_text_with_selections(
 4840            editor,
 4841            indoc! {r#"
 4842                use mod1::mod2::{mod3, mo«ˇ»d4};
 4843
 4844                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4845                    let var1 = "te«ˇ»xt";
 4846                }
 4847            "#},
 4848            cx,
 4849        );
 4850    });
 4851
 4852    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4853    // a fold.
 4854    editor.update(cx, |view, cx| {
 4855        view.fold_ranges(
 4856            vec![
 4857                (
 4858                    Point::new(0, 21)..Point::new(0, 24),
 4859                    FoldPlaceholder::test(),
 4860                ),
 4861                (
 4862                    Point::new(3, 20)..Point::new(3, 22),
 4863                    FoldPlaceholder::test(),
 4864                ),
 4865            ],
 4866            true,
 4867            cx,
 4868        );
 4869        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4870    });
 4871    editor.update(cx, |editor, cx| {
 4872        assert_text_with_selections(
 4873            editor,
 4874            indoc! {r#"
 4875                use mod1::mod2::«{mod3, mod4}ˇ»;
 4876
 4877                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4878                    «let var1 = "text";ˇ»
 4879                }
 4880            "#},
 4881            cx,
 4882        );
 4883    });
 4884}
 4885
 4886#[gpui::test]
 4887async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4888    init_test(cx, |_| {});
 4889
 4890    let language = Arc::new(
 4891        Language::new(
 4892            LanguageConfig {
 4893                brackets: BracketPairConfig {
 4894                    pairs: vec![
 4895                        BracketPair {
 4896                            start: "{".to_string(),
 4897                            end: "}".to_string(),
 4898                            close: false,
 4899                            surround: false,
 4900                            newline: true,
 4901                        },
 4902                        BracketPair {
 4903                            start: "(".to_string(),
 4904                            end: ")".to_string(),
 4905                            close: false,
 4906                            surround: false,
 4907                            newline: true,
 4908                        },
 4909                    ],
 4910                    ..Default::default()
 4911                },
 4912                ..Default::default()
 4913            },
 4914            Some(tree_sitter_rust::language()),
 4915        )
 4916        .with_indents_query(
 4917            r#"
 4918                (_ "(" ")" @end) @indent
 4919                (_ "{" "}" @end) @indent
 4920            "#,
 4921        )
 4922        .unwrap(),
 4923    );
 4924
 4925    let text = "fn a() {}";
 4926
 4927    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4928    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4929    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4930    editor
 4931        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4932        .await;
 4933
 4934    _ = editor.update(cx, |editor, cx| {
 4935        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4936        editor.newline(&Newline, cx);
 4937        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4938        assert_eq!(
 4939            editor.selections.ranges(cx),
 4940            &[
 4941                Point::new(1, 4)..Point::new(1, 4),
 4942                Point::new(3, 4)..Point::new(3, 4),
 4943                Point::new(5, 0)..Point::new(5, 0)
 4944            ]
 4945        );
 4946    });
 4947}
 4948
 4949#[gpui::test]
 4950async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4951    init_test(cx, |_| {});
 4952
 4953    let mut cx = EditorTestContext::new(cx).await;
 4954
 4955    let language = Arc::new(Language::new(
 4956        LanguageConfig {
 4957            brackets: BracketPairConfig {
 4958                pairs: vec![
 4959                    BracketPair {
 4960                        start: "{".to_string(),
 4961                        end: "}".to_string(),
 4962                        close: true,
 4963                        surround: true,
 4964                        newline: true,
 4965                    },
 4966                    BracketPair {
 4967                        start: "(".to_string(),
 4968                        end: ")".to_string(),
 4969                        close: true,
 4970                        surround: true,
 4971                        newline: true,
 4972                    },
 4973                    BracketPair {
 4974                        start: "/*".to_string(),
 4975                        end: " */".to_string(),
 4976                        close: true,
 4977                        surround: true,
 4978                        newline: true,
 4979                    },
 4980                    BracketPair {
 4981                        start: "[".to_string(),
 4982                        end: "]".to_string(),
 4983                        close: false,
 4984                        surround: false,
 4985                        newline: true,
 4986                    },
 4987                    BracketPair {
 4988                        start: "\"".to_string(),
 4989                        end: "\"".to_string(),
 4990                        close: true,
 4991                        surround: true,
 4992                        newline: false,
 4993                    },
 4994                    BracketPair {
 4995                        start: "<".to_string(),
 4996                        end: ">".to_string(),
 4997                        close: false,
 4998                        surround: true,
 4999                        newline: true,
 5000                    },
 5001                ],
 5002                ..Default::default()
 5003            },
 5004            autoclose_before: "})]".to_string(),
 5005            ..Default::default()
 5006        },
 5007        Some(tree_sitter_rust::language()),
 5008    ));
 5009
 5010    cx.language_registry().add(language.clone());
 5011    cx.update_buffer(|buffer, cx| {
 5012        buffer.set_language(Some(language), cx);
 5013    });
 5014
 5015    cx.set_state(
 5016        &r#"
 5017            🏀ˇ
 5018            εˇ
 5019            ❤️ˇ
 5020        "#
 5021        .unindent(),
 5022    );
 5023
 5024    // autoclose multiple nested brackets at multiple cursors
 5025    cx.update_editor(|view, cx| {
 5026        view.handle_input("{", cx);
 5027        view.handle_input("{", cx);
 5028        view.handle_input("{", cx);
 5029    });
 5030    cx.assert_editor_state(
 5031        &"
 5032            🏀{{{ˇ}}}
 5033            ε{{{ˇ}}}
 5034            ❤️{{{ˇ}}}
 5035        "
 5036        .unindent(),
 5037    );
 5038
 5039    // insert a different closing bracket
 5040    cx.update_editor(|view, cx| {
 5041        view.handle_input(")", cx);
 5042    });
 5043    cx.assert_editor_state(
 5044        &"
 5045            🏀{{{)ˇ}}}
 5046            ε{{{)ˇ}}}
 5047            ❤️{{{)ˇ}}}
 5048        "
 5049        .unindent(),
 5050    );
 5051
 5052    // skip over the auto-closed brackets when typing a closing bracket
 5053    cx.update_editor(|view, cx| {
 5054        view.move_right(&MoveRight, cx);
 5055        view.handle_input("}", cx);
 5056        view.handle_input("}", cx);
 5057        view.handle_input("}", cx);
 5058    });
 5059    cx.assert_editor_state(
 5060        &"
 5061            🏀{{{)}}}}ˇ
 5062            ε{{{)}}}}ˇ
 5063            ❤️{{{)}}}}ˇ
 5064        "
 5065        .unindent(),
 5066    );
 5067
 5068    // autoclose multi-character pairs
 5069    cx.set_state(
 5070        &"
 5071            ˇ
 5072            ˇ
 5073        "
 5074        .unindent(),
 5075    );
 5076    cx.update_editor(|view, cx| {
 5077        view.handle_input("/", cx);
 5078        view.handle_input("*", cx);
 5079    });
 5080    cx.assert_editor_state(
 5081        &"
 5082            /*ˇ */
 5083            /*ˇ */
 5084        "
 5085        .unindent(),
 5086    );
 5087
 5088    // one cursor autocloses a multi-character pair, one cursor
 5089    // does not autoclose.
 5090    cx.set_state(
 5091        &"
 5092 5093            ˇ
 5094        "
 5095        .unindent(),
 5096    );
 5097    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5098    cx.assert_editor_state(
 5099        &"
 5100            /*ˇ */
 5101 5102        "
 5103        .unindent(),
 5104    );
 5105
 5106    // Don't autoclose if the next character isn't whitespace and isn't
 5107    // listed in the language's "autoclose_before" section.
 5108    cx.set_state("ˇa b");
 5109    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5110    cx.assert_editor_state("{ˇa b");
 5111
 5112    // Don't autoclose if `close` is false for the bracket pair
 5113    cx.set_state("ˇ");
 5114    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5115    cx.assert_editor_state("");
 5116
 5117    // Surround with brackets if text is selected
 5118    cx.set_state("«aˇ» b");
 5119    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5120    cx.assert_editor_state("{«aˇ»} b");
 5121
 5122    // Autclose pair where the start and end characters are the same
 5123    cx.set_state("");
 5124    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5125    cx.assert_editor_state("a\"ˇ\"");
 5126    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5127    cx.assert_editor_state("a\"\"ˇ");
 5128
 5129    // Don't autoclose pair if autoclose is disabled
 5130    cx.set_state("ˇ");
 5131    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5132    cx.assert_editor_state("");
 5133
 5134    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5135    cx.set_state("«aˇ» b");
 5136    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5137    cx.assert_editor_state("<«aˇ»> b");
 5138}
 5139
 5140#[gpui::test]
 5141async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5142    init_test(cx, |settings| {
 5143        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5144    });
 5145
 5146    let mut cx = EditorTestContext::new(cx).await;
 5147
 5148    let language = Arc::new(Language::new(
 5149        LanguageConfig {
 5150            brackets: BracketPairConfig {
 5151                pairs: vec![
 5152                    BracketPair {
 5153                        start: "{".to_string(),
 5154                        end: "}".to_string(),
 5155                        close: true,
 5156                        surround: true,
 5157                        newline: true,
 5158                    },
 5159                    BracketPair {
 5160                        start: "(".to_string(),
 5161                        end: ")".to_string(),
 5162                        close: true,
 5163                        surround: true,
 5164                        newline: true,
 5165                    },
 5166                    BracketPair {
 5167                        start: "[".to_string(),
 5168                        end: "]".to_string(),
 5169                        close: false,
 5170                        surround: false,
 5171                        newline: true,
 5172                    },
 5173                ],
 5174                ..Default::default()
 5175            },
 5176            autoclose_before: "})]".to_string(),
 5177            ..Default::default()
 5178        },
 5179        Some(tree_sitter_rust::language()),
 5180    ));
 5181
 5182    cx.language_registry().add(language.clone());
 5183    cx.update_buffer(|buffer, cx| {
 5184        buffer.set_language(Some(language), cx);
 5185    });
 5186
 5187    cx.set_state(
 5188        &"
 5189            ˇ
 5190            ˇ
 5191            ˇ
 5192        "
 5193        .unindent(),
 5194    );
 5195
 5196    // ensure only matching closing brackets are skipped over
 5197    cx.update_editor(|view, cx| {
 5198        view.handle_input("}", cx);
 5199        view.move_left(&MoveLeft, cx);
 5200        view.handle_input(")", cx);
 5201        view.move_left(&MoveLeft, cx);
 5202    });
 5203    cx.assert_editor_state(
 5204        &"
 5205            ˇ)}
 5206            ˇ)}
 5207            ˇ)}
 5208        "
 5209        .unindent(),
 5210    );
 5211
 5212    // skip-over closing brackets at multiple cursors
 5213    cx.update_editor(|view, cx| {
 5214        view.handle_input(")", cx);
 5215        view.handle_input("}", cx);
 5216    });
 5217    cx.assert_editor_state(
 5218        &"
 5219            )}ˇ
 5220            )}ˇ
 5221            )}ˇ
 5222        "
 5223        .unindent(),
 5224    );
 5225
 5226    // ignore non-close brackets
 5227    cx.update_editor(|view, cx| {
 5228        view.handle_input("]", cx);
 5229        view.move_left(&MoveLeft, cx);
 5230        view.handle_input("]", cx);
 5231    });
 5232    cx.assert_editor_state(
 5233        &"
 5234            )}]ˇ]
 5235            )}]ˇ]
 5236            )}]ˇ]
 5237        "
 5238        .unindent(),
 5239    );
 5240}
 5241
 5242#[gpui::test]
 5243async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5244    init_test(cx, |_| {});
 5245
 5246    let mut cx = EditorTestContext::new(cx).await;
 5247
 5248    let html_language = Arc::new(
 5249        Language::new(
 5250            LanguageConfig {
 5251                name: "HTML".into(),
 5252                brackets: BracketPairConfig {
 5253                    pairs: vec![
 5254                        BracketPair {
 5255                            start: "<".into(),
 5256                            end: ">".into(),
 5257                            close: true,
 5258                            ..Default::default()
 5259                        },
 5260                        BracketPair {
 5261                            start: "{".into(),
 5262                            end: "}".into(),
 5263                            close: true,
 5264                            ..Default::default()
 5265                        },
 5266                        BracketPair {
 5267                            start: "(".into(),
 5268                            end: ")".into(),
 5269                            close: true,
 5270                            ..Default::default()
 5271                        },
 5272                    ],
 5273                    ..Default::default()
 5274                },
 5275                autoclose_before: "})]>".into(),
 5276                ..Default::default()
 5277            },
 5278            Some(tree_sitter_html::language()),
 5279        )
 5280        .with_injection_query(
 5281            r#"
 5282            (script_element
 5283                (raw_text) @content
 5284                (#set! "language" "javascript"))
 5285            "#,
 5286        )
 5287        .unwrap(),
 5288    );
 5289
 5290    let javascript_language = Arc::new(Language::new(
 5291        LanguageConfig {
 5292            name: "JavaScript".into(),
 5293            brackets: BracketPairConfig {
 5294                pairs: vec![
 5295                    BracketPair {
 5296                        start: "/*".into(),
 5297                        end: " */".into(),
 5298                        close: true,
 5299                        ..Default::default()
 5300                    },
 5301                    BracketPair {
 5302                        start: "{".into(),
 5303                        end: "}".into(),
 5304                        close: true,
 5305                        ..Default::default()
 5306                    },
 5307                    BracketPair {
 5308                        start: "(".into(),
 5309                        end: ")".into(),
 5310                        close: true,
 5311                        ..Default::default()
 5312                    },
 5313                ],
 5314                ..Default::default()
 5315            },
 5316            autoclose_before: "})]>".into(),
 5317            ..Default::default()
 5318        },
 5319        Some(tree_sitter_typescript::language_tsx()),
 5320    ));
 5321
 5322    cx.language_registry().add(html_language.clone());
 5323    cx.language_registry().add(javascript_language.clone());
 5324
 5325    cx.update_buffer(|buffer, cx| {
 5326        buffer.set_language(Some(html_language), cx);
 5327    });
 5328
 5329    cx.set_state(
 5330        &r#"
 5331            <body>ˇ
 5332                <script>
 5333                    var x = 1;ˇ
 5334                </script>
 5335            </body>ˇ
 5336        "#
 5337        .unindent(),
 5338    );
 5339
 5340    // Precondition: different languages are active at different locations.
 5341    cx.update_editor(|editor, cx| {
 5342        let snapshot = editor.snapshot(cx);
 5343        let cursors = editor.selections.ranges::<usize>(cx);
 5344        let languages = cursors
 5345            .iter()
 5346            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5347            .collect::<Vec<_>>();
 5348        assert_eq!(
 5349            languages,
 5350            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5351        );
 5352    });
 5353
 5354    // Angle brackets autoclose in HTML, but not JavaScript.
 5355    cx.update_editor(|editor, cx| {
 5356        editor.handle_input("<", cx);
 5357        editor.handle_input("a", cx);
 5358    });
 5359    cx.assert_editor_state(
 5360        &r#"
 5361            <body><aˇ>
 5362                <script>
 5363                    var x = 1;<aˇ
 5364                </script>
 5365            </body><aˇ>
 5366        "#
 5367        .unindent(),
 5368    );
 5369
 5370    // Curly braces and parens autoclose in both HTML and JavaScript.
 5371    cx.update_editor(|editor, cx| {
 5372        editor.handle_input(" b=", cx);
 5373        editor.handle_input("{", cx);
 5374        editor.handle_input("c", cx);
 5375        editor.handle_input("(", cx);
 5376    });
 5377    cx.assert_editor_state(
 5378        &r#"
 5379            <body><a b={c(ˇ)}>
 5380                <script>
 5381                    var x = 1;<a b={c(ˇ)}
 5382                </script>
 5383            </body><a b={c(ˇ)}>
 5384        "#
 5385        .unindent(),
 5386    );
 5387
 5388    // Brackets that were already autoclosed are skipped.
 5389    cx.update_editor(|editor, cx| {
 5390        editor.handle_input(")", cx);
 5391        editor.handle_input("d", cx);
 5392        editor.handle_input("}", cx);
 5393    });
 5394    cx.assert_editor_state(
 5395        &r#"
 5396            <body><a b={c()d}ˇ>
 5397                <script>
 5398                    var x = 1;<a b={c()d}ˇ
 5399                </script>
 5400            </body><a b={c()d}ˇ>
 5401        "#
 5402        .unindent(),
 5403    );
 5404    cx.update_editor(|editor, cx| {
 5405        editor.handle_input(">", cx);
 5406    });
 5407    cx.assert_editor_state(
 5408        &r#"
 5409            <body><a b={c()d}>ˇ
 5410                <script>
 5411                    var x = 1;<a b={c()d}>ˇ
 5412                </script>
 5413            </body><a b={c()d}>ˇ
 5414        "#
 5415        .unindent(),
 5416    );
 5417
 5418    // Reset
 5419    cx.set_state(
 5420        &r#"
 5421            <body>ˇ
 5422                <script>
 5423                    var x = 1;ˇ
 5424                </script>
 5425            </body>ˇ
 5426        "#
 5427        .unindent(),
 5428    );
 5429
 5430    cx.update_editor(|editor, cx| {
 5431        editor.handle_input("<", cx);
 5432    });
 5433    cx.assert_editor_state(
 5434        &r#"
 5435            <body><ˇ>
 5436                <script>
 5437                    var x = 1;<ˇ
 5438                </script>
 5439            </body><ˇ>
 5440        "#
 5441        .unindent(),
 5442    );
 5443
 5444    // When backspacing, the closing angle brackets are removed.
 5445    cx.update_editor(|editor, cx| {
 5446        editor.backspace(&Backspace, cx);
 5447    });
 5448    cx.assert_editor_state(
 5449        &r#"
 5450            <body>ˇ
 5451                <script>
 5452                    var x = 1;ˇ
 5453                </script>
 5454            </body>ˇ
 5455        "#
 5456        .unindent(),
 5457    );
 5458
 5459    // Block comments autoclose in JavaScript, but not HTML.
 5460    cx.update_editor(|editor, cx| {
 5461        editor.handle_input("/", cx);
 5462        editor.handle_input("*", cx);
 5463    });
 5464    cx.assert_editor_state(
 5465        &r#"
 5466            <body>/*ˇ
 5467                <script>
 5468                    var x = 1;/*ˇ */
 5469                </script>
 5470            </body>/*ˇ
 5471        "#
 5472        .unindent(),
 5473    );
 5474}
 5475
 5476#[gpui::test]
 5477async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5478    init_test(cx, |_| {});
 5479
 5480    let mut cx = EditorTestContext::new(cx).await;
 5481
 5482    let rust_language = Arc::new(
 5483        Language::new(
 5484            LanguageConfig {
 5485                name: "Rust".into(),
 5486                brackets: serde_json::from_value(json!([
 5487                    { "start": "{", "end": "}", "close": true, "newline": true },
 5488                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5489                ]))
 5490                .unwrap(),
 5491                autoclose_before: "})]>".into(),
 5492                ..Default::default()
 5493            },
 5494            Some(tree_sitter_rust::language()),
 5495        )
 5496        .with_override_query("(string_literal) @string")
 5497        .unwrap(),
 5498    );
 5499
 5500    cx.language_registry().add(rust_language.clone());
 5501    cx.update_buffer(|buffer, cx| {
 5502        buffer.set_language(Some(rust_language), cx);
 5503    });
 5504
 5505    cx.set_state(
 5506        &r#"
 5507            let x = ˇ
 5508        "#
 5509        .unindent(),
 5510    );
 5511
 5512    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5513    cx.update_editor(|editor, cx| {
 5514        editor.handle_input("\"", cx);
 5515    });
 5516    cx.assert_editor_state(
 5517        &r#"
 5518            let x = "ˇ"
 5519        "#
 5520        .unindent(),
 5521    );
 5522
 5523    // Inserting another quotation mark. The cursor moves across the existing
 5524    // automatically-inserted quotation mark.
 5525    cx.update_editor(|editor, cx| {
 5526        editor.handle_input("\"", cx);
 5527    });
 5528    cx.assert_editor_state(
 5529        &r#"
 5530            let x = ""ˇ
 5531        "#
 5532        .unindent(),
 5533    );
 5534
 5535    // Reset
 5536    cx.set_state(
 5537        &r#"
 5538            let x = ˇ
 5539        "#
 5540        .unindent(),
 5541    );
 5542
 5543    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5544    cx.update_editor(|editor, cx| {
 5545        editor.handle_input("\"", cx);
 5546        editor.handle_input(" ", cx);
 5547        editor.move_left(&Default::default(), cx);
 5548        editor.handle_input("\\", cx);
 5549        editor.handle_input("\"", cx);
 5550    });
 5551    cx.assert_editor_state(
 5552        &r#"
 5553            let x = "\"ˇ "
 5554        "#
 5555        .unindent(),
 5556    );
 5557
 5558    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5559    // mark. Nothing is inserted.
 5560    cx.update_editor(|editor, cx| {
 5561        editor.move_right(&Default::default(), cx);
 5562        editor.handle_input("\"", cx);
 5563    });
 5564    cx.assert_editor_state(
 5565        &r#"
 5566            let x = "\" "ˇ
 5567        "#
 5568        .unindent(),
 5569    );
 5570}
 5571
 5572#[gpui::test]
 5573async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5574    init_test(cx, |_| {});
 5575
 5576    let language = Arc::new(Language::new(
 5577        LanguageConfig {
 5578            brackets: BracketPairConfig {
 5579                pairs: vec![
 5580                    BracketPair {
 5581                        start: "{".to_string(),
 5582                        end: "}".to_string(),
 5583                        close: true,
 5584                        surround: true,
 5585                        newline: true,
 5586                    },
 5587                    BracketPair {
 5588                        start: "/* ".to_string(),
 5589                        end: "*/".to_string(),
 5590                        close: true,
 5591                        surround: true,
 5592                        ..Default::default()
 5593                    },
 5594                ],
 5595                ..Default::default()
 5596            },
 5597            ..Default::default()
 5598        },
 5599        Some(tree_sitter_rust::language()),
 5600    ));
 5601
 5602    let text = r#"
 5603        a
 5604        b
 5605        c
 5606    "#
 5607    .unindent();
 5608
 5609    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5610    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5611    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5612    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5613        .await;
 5614
 5615    _ = view.update(cx, |view, cx| {
 5616        view.change_selections(None, cx, |s| {
 5617            s.select_display_ranges([
 5618                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5619                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5620                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5621            ])
 5622        });
 5623
 5624        view.handle_input("{", cx);
 5625        view.handle_input("{", cx);
 5626        view.handle_input("{", cx);
 5627        assert_eq!(
 5628            view.text(cx),
 5629            "
 5630                {{{a}}}
 5631                {{{b}}}
 5632                {{{c}}}
 5633            "
 5634            .unindent()
 5635        );
 5636        assert_eq!(
 5637            view.selections.display_ranges(cx),
 5638            [
 5639                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5640                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5641                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5642            ]
 5643        );
 5644
 5645        view.undo(&Undo, cx);
 5646        view.undo(&Undo, cx);
 5647        view.undo(&Undo, cx);
 5648        assert_eq!(
 5649            view.text(cx),
 5650            "
 5651                a
 5652                b
 5653                c
 5654            "
 5655            .unindent()
 5656        );
 5657        assert_eq!(
 5658            view.selections.display_ranges(cx),
 5659            [
 5660                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5661                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5662                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5663            ]
 5664        );
 5665
 5666        // Ensure inserting the first character of a multi-byte bracket pair
 5667        // doesn't surround the selections with the bracket.
 5668        view.handle_input("/", cx);
 5669        assert_eq!(
 5670            view.text(cx),
 5671            "
 5672                /
 5673                /
 5674                /
 5675            "
 5676            .unindent()
 5677        );
 5678        assert_eq!(
 5679            view.selections.display_ranges(cx),
 5680            [
 5681                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5682                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5683                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5684            ]
 5685        );
 5686
 5687        view.undo(&Undo, cx);
 5688        assert_eq!(
 5689            view.text(cx),
 5690            "
 5691                a
 5692                b
 5693                c
 5694            "
 5695            .unindent()
 5696        );
 5697        assert_eq!(
 5698            view.selections.display_ranges(cx),
 5699            [
 5700                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5701                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5702                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5703            ]
 5704        );
 5705
 5706        // Ensure inserting the last character of a multi-byte bracket pair
 5707        // doesn't surround the selections with the bracket.
 5708        view.handle_input("*", cx);
 5709        assert_eq!(
 5710            view.text(cx),
 5711            "
 5712                *
 5713                *
 5714                *
 5715            "
 5716            .unindent()
 5717        );
 5718        assert_eq!(
 5719            view.selections.display_ranges(cx),
 5720            [
 5721                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5722                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5723                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5724            ]
 5725        );
 5726    });
 5727}
 5728
 5729#[gpui::test]
 5730async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5731    init_test(cx, |_| {});
 5732
 5733    let language = Arc::new(Language::new(
 5734        LanguageConfig {
 5735            brackets: BracketPairConfig {
 5736                pairs: vec![BracketPair {
 5737                    start: "{".to_string(),
 5738                    end: "}".to_string(),
 5739                    close: true,
 5740                    surround: true,
 5741                    newline: true,
 5742                }],
 5743                ..Default::default()
 5744            },
 5745            autoclose_before: "}".to_string(),
 5746            ..Default::default()
 5747        },
 5748        Some(tree_sitter_rust::language()),
 5749    ));
 5750
 5751    let text = r#"
 5752        a
 5753        b
 5754        c
 5755    "#
 5756    .unindent();
 5757
 5758    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5759    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5760    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5761    editor
 5762        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5763        .await;
 5764
 5765    _ = editor.update(cx, |editor, cx| {
 5766        editor.change_selections(None, cx, |s| {
 5767            s.select_ranges([
 5768                Point::new(0, 1)..Point::new(0, 1),
 5769                Point::new(1, 1)..Point::new(1, 1),
 5770                Point::new(2, 1)..Point::new(2, 1),
 5771            ])
 5772        });
 5773
 5774        editor.handle_input("{", cx);
 5775        editor.handle_input("{", cx);
 5776        editor.handle_input("_", cx);
 5777        assert_eq!(
 5778            editor.text(cx),
 5779            "
 5780                a{{_}}
 5781                b{{_}}
 5782                c{{_}}
 5783            "
 5784            .unindent()
 5785        );
 5786        assert_eq!(
 5787            editor.selections.ranges::<Point>(cx),
 5788            [
 5789                Point::new(0, 4)..Point::new(0, 4),
 5790                Point::new(1, 4)..Point::new(1, 4),
 5791                Point::new(2, 4)..Point::new(2, 4)
 5792            ]
 5793        );
 5794
 5795        editor.backspace(&Default::default(), cx);
 5796        editor.backspace(&Default::default(), cx);
 5797        assert_eq!(
 5798            editor.text(cx),
 5799            "
 5800                a{}
 5801                b{}
 5802                c{}
 5803            "
 5804            .unindent()
 5805        );
 5806        assert_eq!(
 5807            editor.selections.ranges::<Point>(cx),
 5808            [
 5809                Point::new(0, 2)..Point::new(0, 2),
 5810                Point::new(1, 2)..Point::new(1, 2),
 5811                Point::new(2, 2)..Point::new(2, 2)
 5812            ]
 5813        );
 5814
 5815        editor.delete_to_previous_word_start(&Default::default(), cx);
 5816        assert_eq!(
 5817            editor.text(cx),
 5818            "
 5819                a
 5820                b
 5821                c
 5822            "
 5823            .unindent()
 5824        );
 5825        assert_eq!(
 5826            editor.selections.ranges::<Point>(cx),
 5827            [
 5828                Point::new(0, 1)..Point::new(0, 1),
 5829                Point::new(1, 1)..Point::new(1, 1),
 5830                Point::new(2, 1)..Point::new(2, 1)
 5831            ]
 5832        );
 5833    });
 5834}
 5835
 5836#[gpui::test]
 5837async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5838    init_test(cx, |settings| {
 5839        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5840    });
 5841
 5842    let mut cx = EditorTestContext::new(cx).await;
 5843
 5844    let language = Arc::new(Language::new(
 5845        LanguageConfig {
 5846            brackets: BracketPairConfig {
 5847                pairs: vec![
 5848                    BracketPair {
 5849                        start: "{".to_string(),
 5850                        end: "}".to_string(),
 5851                        close: true,
 5852                        surround: true,
 5853                        newline: true,
 5854                    },
 5855                    BracketPair {
 5856                        start: "(".to_string(),
 5857                        end: ")".to_string(),
 5858                        close: true,
 5859                        surround: true,
 5860                        newline: true,
 5861                    },
 5862                    BracketPair {
 5863                        start: "[".to_string(),
 5864                        end: "]".to_string(),
 5865                        close: false,
 5866                        surround: true,
 5867                        newline: true,
 5868                    },
 5869                ],
 5870                ..Default::default()
 5871            },
 5872            autoclose_before: "})]".to_string(),
 5873            ..Default::default()
 5874        },
 5875        Some(tree_sitter_rust::language()),
 5876    ));
 5877
 5878    cx.language_registry().add(language.clone());
 5879    cx.update_buffer(|buffer, cx| {
 5880        buffer.set_language(Some(language), cx);
 5881    });
 5882
 5883    cx.set_state(
 5884        &"
 5885            {(ˇ)}
 5886            [[ˇ]]
 5887            {(ˇ)}
 5888        "
 5889        .unindent(),
 5890    );
 5891
 5892    cx.update_editor(|view, cx| {
 5893        view.backspace(&Default::default(), cx);
 5894        view.backspace(&Default::default(), cx);
 5895    });
 5896
 5897    cx.assert_editor_state(
 5898        &"
 5899            ˇ
 5900            ˇ]]
 5901            ˇ
 5902        "
 5903        .unindent(),
 5904    );
 5905
 5906    cx.update_editor(|view, cx| {
 5907        view.handle_input("{", cx);
 5908        view.handle_input("{", cx);
 5909        view.move_right(&MoveRight, cx);
 5910        view.move_right(&MoveRight, cx);
 5911        view.move_left(&MoveLeft, cx);
 5912        view.move_left(&MoveLeft, cx);
 5913        view.backspace(&Default::default(), cx);
 5914    });
 5915
 5916    cx.assert_editor_state(
 5917        &"
 5918            {ˇ}
 5919            {ˇ}]]
 5920            {ˇ}
 5921        "
 5922        .unindent(),
 5923    );
 5924
 5925    cx.update_editor(|view, cx| {
 5926        view.backspace(&Default::default(), cx);
 5927    });
 5928
 5929    cx.assert_editor_state(
 5930        &"
 5931            ˇ
 5932            ˇ]]
 5933            ˇ
 5934        "
 5935        .unindent(),
 5936    );
 5937}
 5938
 5939#[gpui::test]
 5940async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5941    init_test(cx, |_| {});
 5942
 5943    let language = Arc::new(Language::new(
 5944        LanguageConfig::default(),
 5945        Some(tree_sitter_rust::language()),
 5946    ));
 5947
 5948    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5949    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5950    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5951    editor
 5952        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5953        .await;
 5954
 5955    _ = editor.update(cx, |editor, cx| {
 5956        editor.set_auto_replace_emoji_shortcode(true);
 5957
 5958        editor.handle_input("Hello ", cx);
 5959        editor.handle_input(":wave", cx);
 5960        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5961
 5962        editor.handle_input(":", cx);
 5963        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5964
 5965        editor.handle_input(" :smile", cx);
 5966        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5967
 5968        editor.handle_input(":", cx);
 5969        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5970
 5971        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5972        editor.handle_input(":wave", cx);
 5973        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5974
 5975        editor.handle_input(":", cx);
 5976        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5977
 5978        editor.handle_input(":1", cx);
 5979        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5980
 5981        editor.handle_input(":", cx);
 5982        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5983
 5984        // Ensure shortcode does not get replaced when it is part of a word
 5985        editor.handle_input(" Test:wave", cx);
 5986        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5987
 5988        editor.handle_input(":", cx);
 5989        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5990
 5991        editor.set_auto_replace_emoji_shortcode(false);
 5992
 5993        // Ensure shortcode does not get replaced when auto replace is off
 5994        editor.handle_input(" :wave", cx);
 5995        assert_eq!(
 5996            editor.text(cx),
 5997            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5998        );
 5999
 6000        editor.handle_input(":", cx);
 6001        assert_eq!(
 6002            editor.text(cx),
 6003            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6004        );
 6005    });
 6006}
 6007
 6008#[gpui::test]
 6009async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6010    init_test(cx, |_| {});
 6011
 6012    let (text, insertion_ranges) = marked_text_ranges(
 6013        indoc! {"
 6014            a.ˇ b
 6015            a.ˇ b
 6016            a.ˇ b
 6017        "},
 6018        false,
 6019    );
 6020
 6021    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6022    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6023
 6024    _ = editor.update(cx, |editor, cx| {
 6025        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6026
 6027        editor
 6028            .insert_snippet(&insertion_ranges, snippet, cx)
 6029            .unwrap();
 6030
 6031        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6032            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6033            assert_eq!(editor.text(cx), expected_text);
 6034            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6035        }
 6036
 6037        assert(
 6038            editor,
 6039            cx,
 6040            indoc! {"
 6041                a.f(«one», two, «three») b
 6042                a.f(«one», two, «three») b
 6043                a.f(«one», two, «three») b
 6044            "},
 6045        );
 6046
 6047        // Can't move earlier than the first tab stop
 6048        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6049        assert(
 6050            editor,
 6051            cx,
 6052            indoc! {"
 6053                a.f(«one», two, «three») b
 6054                a.f(«one», two, «three») b
 6055                a.f(«one», two, «three») b
 6056            "},
 6057        );
 6058
 6059        assert!(editor.move_to_next_snippet_tabstop(cx));
 6060        assert(
 6061            editor,
 6062            cx,
 6063            indoc! {"
 6064                a.f(one, «two», three) b
 6065                a.f(one, «two», three) b
 6066                a.f(one, «two», three) b
 6067            "},
 6068        );
 6069
 6070        editor.move_to_prev_snippet_tabstop(cx);
 6071        assert(
 6072            editor,
 6073            cx,
 6074            indoc! {"
 6075                a.f(«one», two, «three») b
 6076                a.f(«one», two, «three») b
 6077                a.f(«one», two, «three») b
 6078            "},
 6079        );
 6080
 6081        assert!(editor.move_to_next_snippet_tabstop(cx));
 6082        assert(
 6083            editor,
 6084            cx,
 6085            indoc! {"
 6086                a.f(one, «two», three) b
 6087                a.f(one, «two», three) b
 6088                a.f(one, «two», three) b
 6089            "},
 6090        );
 6091        assert!(editor.move_to_next_snippet_tabstop(cx));
 6092        assert(
 6093            editor,
 6094            cx,
 6095            indoc! {"
 6096                a.f(one, two, three)ˇ b
 6097                a.f(one, two, three)ˇ b
 6098                a.f(one, two, three)ˇ b
 6099            "},
 6100        );
 6101
 6102        // As soon as the last tab stop is reached, snippet state is gone
 6103        editor.move_to_prev_snippet_tabstop(cx);
 6104        assert(
 6105            editor,
 6106            cx,
 6107            indoc! {"
 6108                a.f(one, two, three)ˇ b
 6109                a.f(one, two, three)ˇ b
 6110                a.f(one, two, three)ˇ b
 6111            "},
 6112        );
 6113    });
 6114}
 6115
 6116#[gpui::test]
 6117async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6118    init_test(cx, |_| {});
 6119
 6120    let fs = FakeFs::new(cx.executor());
 6121    fs.insert_file("/file.rs", Default::default()).await;
 6122
 6123    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6124
 6125    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6126    language_registry.add(rust_lang());
 6127    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6128        "Rust",
 6129        FakeLspAdapter {
 6130            capabilities: lsp::ServerCapabilities {
 6131                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6132                ..Default::default()
 6133            },
 6134            ..Default::default()
 6135        },
 6136    );
 6137
 6138    let buffer = project
 6139        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6140        .await
 6141        .unwrap();
 6142
 6143    cx.executor().start_waiting();
 6144    let fake_server = fake_servers.next().await.unwrap();
 6145
 6146    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6147    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6148    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6149    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6150
 6151    let save = editor
 6152        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6153        .unwrap();
 6154    fake_server
 6155        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6156            assert_eq!(
 6157                params.text_document.uri,
 6158                lsp::Url::from_file_path("/file.rs").unwrap()
 6159            );
 6160            assert_eq!(params.options.tab_size, 4);
 6161            Ok(Some(vec![lsp::TextEdit::new(
 6162                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6163                ", ".to_string(),
 6164            )]))
 6165        })
 6166        .next()
 6167        .await;
 6168    cx.executor().start_waiting();
 6169    save.await;
 6170
 6171    assert_eq!(
 6172        editor.update(cx, |editor, cx| editor.text(cx)),
 6173        "one, two\nthree\n"
 6174    );
 6175    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6176
 6177    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6178    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6179
 6180    // Ensure we can still save even if formatting hangs.
 6181    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6182        assert_eq!(
 6183            params.text_document.uri,
 6184            lsp::Url::from_file_path("/file.rs").unwrap()
 6185        );
 6186        futures::future::pending::<()>().await;
 6187        unreachable!()
 6188    });
 6189    let save = editor
 6190        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6191        .unwrap();
 6192    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6193    cx.executor().start_waiting();
 6194    save.await;
 6195    assert_eq!(
 6196        editor.update(cx, |editor, cx| editor.text(cx)),
 6197        "one\ntwo\nthree\n"
 6198    );
 6199    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6200
 6201    // For non-dirty buffer, no formatting request should be sent
 6202    let save = editor
 6203        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6204        .unwrap();
 6205    let _pending_format_request = fake_server
 6206        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6207            panic!("Should not be invoked on non-dirty buffer");
 6208        })
 6209        .next();
 6210    cx.executor().start_waiting();
 6211    save.await;
 6212
 6213    // Set rust language override and assert overridden tabsize is sent to language server
 6214    update_test_language_settings(cx, |settings| {
 6215        settings.languages.insert(
 6216            "Rust".into(),
 6217            LanguageSettingsContent {
 6218                tab_size: NonZeroU32::new(8),
 6219                ..Default::default()
 6220            },
 6221        );
 6222    });
 6223
 6224    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6225    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6226    let save = editor
 6227        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6228        .unwrap();
 6229    fake_server
 6230        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6231            assert_eq!(
 6232                params.text_document.uri,
 6233                lsp::Url::from_file_path("/file.rs").unwrap()
 6234            );
 6235            assert_eq!(params.options.tab_size, 8);
 6236            Ok(Some(vec![]))
 6237        })
 6238        .next()
 6239        .await;
 6240    cx.executor().start_waiting();
 6241    save.await;
 6242}
 6243
 6244#[gpui::test]
 6245async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6246    init_test(cx, |_| {});
 6247
 6248    let cols = 4;
 6249    let rows = 10;
 6250    let sample_text_1 = sample_text(rows, cols, 'a');
 6251    assert_eq!(
 6252        sample_text_1,
 6253        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6254    );
 6255    let sample_text_2 = sample_text(rows, cols, 'l');
 6256    assert_eq!(
 6257        sample_text_2,
 6258        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6259    );
 6260    let sample_text_3 = sample_text(rows, cols, 'v');
 6261    assert_eq!(
 6262        sample_text_3,
 6263        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6264    );
 6265
 6266    let fs = FakeFs::new(cx.executor());
 6267    fs.insert_tree(
 6268        "/a",
 6269        json!({
 6270            "main.rs": sample_text_1,
 6271            "other.rs": sample_text_2,
 6272            "lib.rs": sample_text_3,
 6273        }),
 6274    )
 6275    .await;
 6276
 6277    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6278    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6279    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6280
 6281    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6282    language_registry.add(rust_lang());
 6283    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6284        "Rust",
 6285        FakeLspAdapter {
 6286            capabilities: lsp::ServerCapabilities {
 6287                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6288                ..Default::default()
 6289            },
 6290            ..Default::default()
 6291        },
 6292    );
 6293
 6294    let worktree = project.update(cx, |project, cx| {
 6295        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6296        assert_eq!(worktrees.len(), 1);
 6297        worktrees.pop().unwrap()
 6298    });
 6299    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6300
 6301    let buffer_1 = project
 6302        .update(cx, |project, cx| {
 6303            project.open_buffer((worktree_id, "main.rs"), cx)
 6304        })
 6305        .await
 6306        .unwrap();
 6307    let buffer_2 = project
 6308        .update(cx, |project, cx| {
 6309            project.open_buffer((worktree_id, "other.rs"), cx)
 6310        })
 6311        .await
 6312        .unwrap();
 6313    let buffer_3 = project
 6314        .update(cx, |project, cx| {
 6315            project.open_buffer((worktree_id, "lib.rs"), cx)
 6316        })
 6317        .await
 6318        .unwrap();
 6319
 6320    let multi_buffer = cx.new_model(|cx| {
 6321        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6322        multi_buffer.push_excerpts(
 6323            buffer_1.clone(),
 6324            [
 6325                ExcerptRange {
 6326                    context: Point::new(0, 0)..Point::new(3, 0),
 6327                    primary: None,
 6328                },
 6329                ExcerptRange {
 6330                    context: Point::new(5, 0)..Point::new(7, 0),
 6331                    primary: None,
 6332                },
 6333                ExcerptRange {
 6334                    context: Point::new(9, 0)..Point::new(10, 4),
 6335                    primary: None,
 6336                },
 6337            ],
 6338            cx,
 6339        );
 6340        multi_buffer.push_excerpts(
 6341            buffer_2.clone(),
 6342            [
 6343                ExcerptRange {
 6344                    context: Point::new(0, 0)..Point::new(3, 0),
 6345                    primary: None,
 6346                },
 6347                ExcerptRange {
 6348                    context: Point::new(5, 0)..Point::new(7, 0),
 6349                    primary: None,
 6350                },
 6351                ExcerptRange {
 6352                    context: Point::new(9, 0)..Point::new(10, 4),
 6353                    primary: None,
 6354                },
 6355            ],
 6356            cx,
 6357        );
 6358        multi_buffer.push_excerpts(
 6359            buffer_3.clone(),
 6360            [
 6361                ExcerptRange {
 6362                    context: Point::new(0, 0)..Point::new(3, 0),
 6363                    primary: None,
 6364                },
 6365                ExcerptRange {
 6366                    context: Point::new(5, 0)..Point::new(7, 0),
 6367                    primary: None,
 6368                },
 6369                ExcerptRange {
 6370                    context: Point::new(9, 0)..Point::new(10, 4),
 6371                    primary: None,
 6372                },
 6373            ],
 6374            cx,
 6375        );
 6376        multi_buffer
 6377    });
 6378    let multi_buffer_editor = cx.new_view(|cx| {
 6379        Editor::new(
 6380            EditorMode::Full,
 6381            multi_buffer,
 6382            Some(project.clone()),
 6383            true,
 6384            cx,
 6385        )
 6386    });
 6387
 6388    multi_buffer_editor.update(cx, |editor, cx| {
 6389        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6390        editor.insert("|one|two|three|", cx);
 6391    });
 6392    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6393    multi_buffer_editor.update(cx, |editor, cx| {
 6394        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6395            s.select_ranges(Some(60..70))
 6396        });
 6397        editor.insert("|four|five|six|", cx);
 6398    });
 6399    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6400
 6401    // First two buffers should be edited, but not the third one.
 6402    assert_eq!(
 6403        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6404        "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}",
 6405    );
 6406    buffer_1.update(cx, |buffer, _| {
 6407        assert!(buffer.is_dirty());
 6408        assert_eq!(
 6409            buffer.text(),
 6410            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6411        )
 6412    });
 6413    buffer_2.update(cx, |buffer, _| {
 6414        assert!(buffer.is_dirty());
 6415        assert_eq!(
 6416            buffer.text(),
 6417            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6418        )
 6419    });
 6420    buffer_3.update(cx, |buffer, _| {
 6421        assert!(!buffer.is_dirty());
 6422        assert_eq!(buffer.text(), sample_text_3,)
 6423    });
 6424
 6425    cx.executor().start_waiting();
 6426    let save = multi_buffer_editor
 6427        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6428        .unwrap();
 6429
 6430    let fake_server = fake_servers.next().await.unwrap();
 6431    fake_server
 6432        .server
 6433        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6434            Ok(Some(vec![lsp::TextEdit::new(
 6435                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6436                format!("[{} formatted]", params.text_document.uri),
 6437            )]))
 6438        })
 6439        .detach();
 6440    save.await;
 6441
 6442    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6443    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6444    assert_eq!(
 6445        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6446        "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}",
 6447    );
 6448    buffer_1.update(cx, |buffer, _| {
 6449        assert!(!buffer.is_dirty());
 6450        assert_eq!(
 6451            buffer.text(),
 6452            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6453        )
 6454    });
 6455    buffer_2.update(cx, |buffer, _| {
 6456        assert!(!buffer.is_dirty());
 6457        assert_eq!(
 6458            buffer.text(),
 6459            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6460        )
 6461    });
 6462    buffer_3.update(cx, |buffer, _| {
 6463        assert!(!buffer.is_dirty());
 6464        assert_eq!(buffer.text(), sample_text_3,)
 6465    });
 6466}
 6467
 6468#[gpui::test]
 6469async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6470    init_test(cx, |_| {});
 6471
 6472    let fs = FakeFs::new(cx.executor());
 6473    fs.insert_file("/file.rs", Default::default()).await;
 6474
 6475    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6476
 6477    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6478    language_registry.add(rust_lang());
 6479    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6480        "Rust",
 6481        FakeLspAdapter {
 6482            capabilities: lsp::ServerCapabilities {
 6483                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6484                ..Default::default()
 6485            },
 6486            ..Default::default()
 6487        },
 6488    );
 6489
 6490    let buffer = project
 6491        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6492        .await
 6493        .unwrap();
 6494
 6495    cx.executor().start_waiting();
 6496    let fake_server = fake_servers.next().await.unwrap();
 6497
 6498    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6499    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6500    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6501    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6502
 6503    let save = editor
 6504        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6505        .unwrap();
 6506    fake_server
 6507        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6508            assert_eq!(
 6509                params.text_document.uri,
 6510                lsp::Url::from_file_path("/file.rs").unwrap()
 6511            );
 6512            assert_eq!(params.options.tab_size, 4);
 6513            Ok(Some(vec![lsp::TextEdit::new(
 6514                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6515                ", ".to_string(),
 6516            )]))
 6517        })
 6518        .next()
 6519        .await;
 6520    cx.executor().start_waiting();
 6521    save.await;
 6522    assert_eq!(
 6523        editor.update(cx, |editor, cx| editor.text(cx)),
 6524        "one, two\nthree\n"
 6525    );
 6526    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6527
 6528    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6529    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6530
 6531    // Ensure we can still save even if formatting hangs.
 6532    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6533        move |params, _| async move {
 6534            assert_eq!(
 6535                params.text_document.uri,
 6536                lsp::Url::from_file_path("/file.rs").unwrap()
 6537            );
 6538            futures::future::pending::<()>().await;
 6539            unreachable!()
 6540        },
 6541    );
 6542    let save = editor
 6543        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6544        .unwrap();
 6545    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6546    cx.executor().start_waiting();
 6547    save.await;
 6548    assert_eq!(
 6549        editor.update(cx, |editor, cx| editor.text(cx)),
 6550        "one\ntwo\nthree\n"
 6551    );
 6552    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6553
 6554    // For non-dirty buffer, no formatting request should be sent
 6555    let save = editor
 6556        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6557        .unwrap();
 6558    let _pending_format_request = fake_server
 6559        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6560            panic!("Should not be invoked on non-dirty buffer");
 6561        })
 6562        .next();
 6563    cx.executor().start_waiting();
 6564    save.await;
 6565
 6566    // Set Rust language override and assert overridden tabsize is sent to language server
 6567    update_test_language_settings(cx, |settings| {
 6568        settings.languages.insert(
 6569            "Rust".into(),
 6570            LanguageSettingsContent {
 6571                tab_size: NonZeroU32::new(8),
 6572                ..Default::default()
 6573            },
 6574        );
 6575    });
 6576
 6577    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6578    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6579    let save = editor
 6580        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6581        .unwrap();
 6582    fake_server
 6583        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6584            assert_eq!(
 6585                params.text_document.uri,
 6586                lsp::Url::from_file_path("/file.rs").unwrap()
 6587            );
 6588            assert_eq!(params.options.tab_size, 8);
 6589            Ok(Some(vec![]))
 6590        })
 6591        .next()
 6592        .await;
 6593    cx.executor().start_waiting();
 6594    save.await;
 6595}
 6596
 6597#[gpui::test]
 6598async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6599    init_test(cx, |settings| {
 6600        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6601            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6602        ))
 6603    });
 6604
 6605    let fs = FakeFs::new(cx.executor());
 6606    fs.insert_file("/file.rs", Default::default()).await;
 6607
 6608    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6609
 6610    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6611    language_registry.add(Arc::new(Language::new(
 6612        LanguageConfig {
 6613            name: "Rust".into(),
 6614            matcher: LanguageMatcher {
 6615                path_suffixes: vec!["rs".to_string()],
 6616                ..Default::default()
 6617            },
 6618            ..LanguageConfig::default()
 6619        },
 6620        Some(tree_sitter_rust::language()),
 6621    )));
 6622    update_test_language_settings(cx, |settings| {
 6623        // Enable Prettier formatting for the same buffer, and ensure
 6624        // LSP is called instead of Prettier.
 6625        settings.defaults.prettier = Some(PrettierSettings {
 6626            allowed: true,
 6627            ..PrettierSettings::default()
 6628        });
 6629    });
 6630    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6631        "Rust",
 6632        FakeLspAdapter {
 6633            capabilities: lsp::ServerCapabilities {
 6634                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6635                ..Default::default()
 6636            },
 6637            ..Default::default()
 6638        },
 6639    );
 6640
 6641    let buffer = project
 6642        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6643        .await
 6644        .unwrap();
 6645
 6646    cx.executor().start_waiting();
 6647    let fake_server = fake_servers.next().await.unwrap();
 6648
 6649    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6650    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6651    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6652
 6653    let format = editor
 6654        .update(cx, |editor, cx| {
 6655            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6656        })
 6657        .unwrap();
 6658    fake_server
 6659        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6660            assert_eq!(
 6661                params.text_document.uri,
 6662                lsp::Url::from_file_path("/file.rs").unwrap()
 6663            );
 6664            assert_eq!(params.options.tab_size, 4);
 6665            Ok(Some(vec![lsp::TextEdit::new(
 6666                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6667                ", ".to_string(),
 6668            )]))
 6669        })
 6670        .next()
 6671        .await;
 6672    cx.executor().start_waiting();
 6673    format.await;
 6674    assert_eq!(
 6675        editor.update(cx, |editor, cx| editor.text(cx)),
 6676        "one, two\nthree\n"
 6677    );
 6678
 6679    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6680    // Ensure we don't lock if formatting hangs.
 6681    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6682        assert_eq!(
 6683            params.text_document.uri,
 6684            lsp::Url::from_file_path("/file.rs").unwrap()
 6685        );
 6686        futures::future::pending::<()>().await;
 6687        unreachable!()
 6688    });
 6689    let format = editor
 6690        .update(cx, |editor, cx| {
 6691            editor.perform_format(project, FormatTrigger::Manual, cx)
 6692        })
 6693        .unwrap();
 6694    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6695    cx.executor().start_waiting();
 6696    format.await;
 6697    assert_eq!(
 6698        editor.update(cx, |editor, cx| editor.text(cx)),
 6699        "one\ntwo\nthree\n"
 6700    );
 6701}
 6702
 6703#[gpui::test]
 6704async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6705    init_test(cx, |_| {});
 6706
 6707    let mut cx = EditorLspTestContext::new_rust(
 6708        lsp::ServerCapabilities {
 6709            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6710            ..Default::default()
 6711        },
 6712        cx,
 6713    )
 6714    .await;
 6715
 6716    cx.set_state(indoc! {"
 6717        one.twoˇ
 6718    "});
 6719
 6720    // The format request takes a long time. When it completes, it inserts
 6721    // a newline and an indent before the `.`
 6722    cx.lsp
 6723        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6724            let executor = cx.background_executor().clone();
 6725            async move {
 6726                executor.timer(Duration::from_millis(100)).await;
 6727                Ok(Some(vec![lsp::TextEdit {
 6728                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6729                    new_text: "\n    ".into(),
 6730                }]))
 6731            }
 6732        });
 6733
 6734    // Submit a format request.
 6735    let format_1 = cx
 6736        .update_editor(|editor, cx| editor.format(&Format, cx))
 6737        .unwrap();
 6738    cx.executor().run_until_parked();
 6739
 6740    // Submit a second format request.
 6741    let format_2 = cx
 6742        .update_editor(|editor, cx| editor.format(&Format, cx))
 6743        .unwrap();
 6744    cx.executor().run_until_parked();
 6745
 6746    // Wait for both format requests to complete
 6747    cx.executor().advance_clock(Duration::from_millis(200));
 6748    cx.executor().start_waiting();
 6749    format_1.await.unwrap();
 6750    cx.executor().start_waiting();
 6751    format_2.await.unwrap();
 6752
 6753    // The formatting edits only happens once.
 6754    cx.assert_editor_state(indoc! {"
 6755        one
 6756            .twoˇ
 6757    "});
 6758}
 6759
 6760#[gpui::test]
 6761async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6762    init_test(cx, |settings| {
 6763        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6764    });
 6765
 6766    let mut cx = EditorLspTestContext::new_rust(
 6767        lsp::ServerCapabilities {
 6768            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6769            ..Default::default()
 6770        },
 6771        cx,
 6772    )
 6773    .await;
 6774
 6775    // Set up a buffer white some trailing whitespace and no trailing newline.
 6776    cx.set_state(
 6777        &[
 6778            "one ",   //
 6779            "twoˇ",   //
 6780            "three ", //
 6781            "four",   //
 6782        ]
 6783        .join("\n"),
 6784    );
 6785
 6786    // Submit a format request.
 6787    let format = cx
 6788        .update_editor(|editor, cx| editor.format(&Format, cx))
 6789        .unwrap();
 6790
 6791    // Record which buffer changes have been sent to the language server
 6792    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6793    cx.lsp
 6794        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6795            let buffer_changes = buffer_changes.clone();
 6796            move |params, _| {
 6797                buffer_changes.lock().extend(
 6798                    params
 6799                        .content_changes
 6800                        .into_iter()
 6801                        .map(|e| (e.range.unwrap(), e.text)),
 6802                );
 6803            }
 6804        });
 6805
 6806    // Handle formatting requests to the language server.
 6807    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6808        let buffer_changes = buffer_changes.clone();
 6809        move |_, _| {
 6810            // When formatting is requested, trailing whitespace has already been stripped,
 6811            // and the trailing newline has already been added.
 6812            assert_eq!(
 6813                &buffer_changes.lock()[1..],
 6814                &[
 6815                    (
 6816                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6817                        "".into()
 6818                    ),
 6819                    (
 6820                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6821                        "".into()
 6822                    ),
 6823                    (
 6824                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6825                        "\n".into()
 6826                    ),
 6827                ]
 6828            );
 6829
 6830            // Insert blank lines between each line of the buffer.
 6831            async move {
 6832                Ok(Some(vec![
 6833                    lsp::TextEdit {
 6834                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6835                        new_text: "\n".into(),
 6836                    },
 6837                    lsp::TextEdit {
 6838                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6839                        new_text: "\n".into(),
 6840                    },
 6841                ]))
 6842            }
 6843        }
 6844    });
 6845
 6846    // After formatting the buffer, the trailing whitespace is stripped,
 6847    // a newline is appended, and the edits provided by the language server
 6848    // have been applied.
 6849    format.await.unwrap();
 6850    cx.assert_editor_state(
 6851        &[
 6852            "one",   //
 6853            "",      //
 6854            "twoˇ",  //
 6855            "",      //
 6856            "three", //
 6857            "four",  //
 6858            "",      //
 6859        ]
 6860        .join("\n"),
 6861    );
 6862
 6863    // Undoing the formatting undoes the trailing whitespace removal, the
 6864    // trailing newline, and the LSP edits.
 6865    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6866    cx.assert_editor_state(
 6867        &[
 6868            "one ",   //
 6869            "twoˇ",   //
 6870            "three ", //
 6871            "four",   //
 6872        ]
 6873        .join("\n"),
 6874    );
 6875}
 6876
 6877#[gpui::test]
 6878async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6879    cx: &mut gpui::TestAppContext,
 6880) {
 6881    init_test(cx, |_| {});
 6882
 6883    cx.update(|cx| {
 6884        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6885            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6886                settings.auto_signature_help = Some(true);
 6887            });
 6888        });
 6889    });
 6890
 6891    let mut cx = EditorLspTestContext::new_rust(
 6892        lsp::ServerCapabilities {
 6893            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6894                ..Default::default()
 6895            }),
 6896            ..Default::default()
 6897        },
 6898        cx,
 6899    )
 6900    .await;
 6901
 6902    let language = Language::new(
 6903        LanguageConfig {
 6904            name: "Rust".into(),
 6905            brackets: BracketPairConfig {
 6906                pairs: vec![
 6907                    BracketPair {
 6908                        start: "{".to_string(),
 6909                        end: "}".to_string(),
 6910                        close: true,
 6911                        surround: true,
 6912                        newline: true,
 6913                    },
 6914                    BracketPair {
 6915                        start: "(".to_string(),
 6916                        end: ")".to_string(),
 6917                        close: true,
 6918                        surround: true,
 6919                        newline: true,
 6920                    },
 6921                    BracketPair {
 6922                        start: "/*".to_string(),
 6923                        end: " */".to_string(),
 6924                        close: true,
 6925                        surround: true,
 6926                        newline: true,
 6927                    },
 6928                    BracketPair {
 6929                        start: "[".to_string(),
 6930                        end: "]".to_string(),
 6931                        close: false,
 6932                        surround: false,
 6933                        newline: true,
 6934                    },
 6935                    BracketPair {
 6936                        start: "\"".to_string(),
 6937                        end: "\"".to_string(),
 6938                        close: true,
 6939                        surround: true,
 6940                        newline: false,
 6941                    },
 6942                    BracketPair {
 6943                        start: "<".to_string(),
 6944                        end: ">".to_string(),
 6945                        close: false,
 6946                        surround: true,
 6947                        newline: true,
 6948                    },
 6949                ],
 6950                ..Default::default()
 6951            },
 6952            autoclose_before: "})]".to_string(),
 6953            ..Default::default()
 6954        },
 6955        Some(tree_sitter_rust::language()),
 6956    );
 6957    let language = Arc::new(language);
 6958
 6959    cx.language_registry().add(language.clone());
 6960    cx.update_buffer(|buffer, cx| {
 6961        buffer.set_language(Some(language), cx);
 6962    });
 6963
 6964    cx.set_state(
 6965        &r#"
 6966            fn main() {
 6967                sampleˇ
 6968            }
 6969        "#
 6970        .unindent(),
 6971    );
 6972
 6973    cx.update_editor(|view, cx| {
 6974        view.handle_input("(", cx);
 6975    });
 6976    cx.assert_editor_state(
 6977        &"
 6978            fn main() {
 6979                sample(ˇ)
 6980            }
 6981        "
 6982        .unindent(),
 6983    );
 6984
 6985    let mocked_response = lsp::SignatureHelp {
 6986        signatures: vec![lsp::SignatureInformation {
 6987            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6988            documentation: None,
 6989            parameters: Some(vec![
 6990                lsp::ParameterInformation {
 6991                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6992                    documentation: None,
 6993                },
 6994                lsp::ParameterInformation {
 6995                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6996                    documentation: None,
 6997                },
 6998            ]),
 6999            active_parameter: None,
 7000        }],
 7001        active_signature: Some(0),
 7002        active_parameter: Some(0),
 7003    };
 7004    handle_signature_help_request(&mut cx, mocked_response).await;
 7005
 7006    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7007        .await;
 7008
 7009    cx.editor(|editor, _| {
 7010        let signature_help_state = editor.signature_help_state.popover().cloned();
 7011        assert!(signature_help_state.is_some());
 7012        let ParsedMarkdown {
 7013            text, highlights, ..
 7014        } = signature_help_state.unwrap().parsed_content;
 7015        assert_eq!(text, "param1: u8, param2: u8");
 7016        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7017    });
 7018}
 7019
 7020#[gpui::test]
 7021async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7022    init_test(cx, |_| {});
 7023
 7024    cx.update(|cx| {
 7025        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7026            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7027                settings.auto_signature_help = Some(false);
 7028                settings.show_signature_help_after_edits = Some(false);
 7029            });
 7030        });
 7031    });
 7032
 7033    let mut cx = EditorLspTestContext::new_rust(
 7034        lsp::ServerCapabilities {
 7035            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7036                ..Default::default()
 7037            }),
 7038            ..Default::default()
 7039        },
 7040        cx,
 7041    )
 7042    .await;
 7043
 7044    let language = Language::new(
 7045        LanguageConfig {
 7046            name: "Rust".into(),
 7047            brackets: BracketPairConfig {
 7048                pairs: vec![
 7049                    BracketPair {
 7050                        start: "{".to_string(),
 7051                        end: "}".to_string(),
 7052                        close: true,
 7053                        surround: true,
 7054                        newline: true,
 7055                    },
 7056                    BracketPair {
 7057                        start: "(".to_string(),
 7058                        end: ")".to_string(),
 7059                        close: true,
 7060                        surround: true,
 7061                        newline: true,
 7062                    },
 7063                    BracketPair {
 7064                        start: "/*".to_string(),
 7065                        end: " */".to_string(),
 7066                        close: true,
 7067                        surround: true,
 7068                        newline: true,
 7069                    },
 7070                    BracketPair {
 7071                        start: "[".to_string(),
 7072                        end: "]".to_string(),
 7073                        close: false,
 7074                        surround: false,
 7075                        newline: true,
 7076                    },
 7077                    BracketPair {
 7078                        start: "\"".to_string(),
 7079                        end: "\"".to_string(),
 7080                        close: true,
 7081                        surround: true,
 7082                        newline: false,
 7083                    },
 7084                    BracketPair {
 7085                        start: "<".to_string(),
 7086                        end: ">".to_string(),
 7087                        close: false,
 7088                        surround: true,
 7089                        newline: true,
 7090                    },
 7091                ],
 7092                ..Default::default()
 7093            },
 7094            autoclose_before: "})]".to_string(),
 7095            ..Default::default()
 7096        },
 7097        Some(tree_sitter_rust::language()),
 7098    );
 7099    let language = Arc::new(language);
 7100
 7101    cx.language_registry().add(language.clone());
 7102    cx.update_buffer(|buffer, cx| {
 7103        buffer.set_language(Some(language), cx);
 7104    });
 7105
 7106    // Ensure that signature_help is not called when no signature help is enabled.
 7107    cx.set_state(
 7108        &r#"
 7109            fn main() {
 7110                sampleˇ
 7111            }
 7112        "#
 7113        .unindent(),
 7114    );
 7115    cx.update_editor(|view, cx| {
 7116        view.handle_input("(", cx);
 7117    });
 7118    cx.assert_editor_state(
 7119        &"
 7120            fn main() {
 7121                sample(ˇ)
 7122            }
 7123        "
 7124        .unindent(),
 7125    );
 7126    cx.editor(|editor, _| {
 7127        assert!(editor.signature_help_state.task().is_none());
 7128    });
 7129
 7130    let mocked_response = lsp::SignatureHelp {
 7131        signatures: vec![lsp::SignatureInformation {
 7132            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7133            documentation: None,
 7134            parameters: Some(vec![
 7135                lsp::ParameterInformation {
 7136                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7137                    documentation: None,
 7138                },
 7139                lsp::ParameterInformation {
 7140                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7141                    documentation: None,
 7142                },
 7143            ]),
 7144            active_parameter: None,
 7145        }],
 7146        active_signature: Some(0),
 7147        active_parameter: Some(0),
 7148    };
 7149
 7150    // Ensure that signature_help is called when enabled afte edits
 7151    cx.update(|cx| {
 7152        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7153            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7154                settings.auto_signature_help = Some(false);
 7155                settings.show_signature_help_after_edits = Some(true);
 7156            });
 7157        });
 7158    });
 7159    cx.set_state(
 7160        &r#"
 7161            fn main() {
 7162                sampleˇ
 7163            }
 7164        "#
 7165        .unindent(),
 7166    );
 7167    cx.update_editor(|view, cx| {
 7168        view.handle_input("(", cx);
 7169    });
 7170    cx.assert_editor_state(
 7171        &"
 7172            fn main() {
 7173                sample(ˇ)
 7174            }
 7175        "
 7176        .unindent(),
 7177    );
 7178    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7179    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7180        .await;
 7181    cx.update_editor(|editor, _| {
 7182        let signature_help_state = editor.signature_help_state.popover().cloned();
 7183        assert!(signature_help_state.is_some());
 7184        let ParsedMarkdown {
 7185            text, highlights, ..
 7186        } = signature_help_state.unwrap().parsed_content;
 7187        assert_eq!(text, "param1: u8, param2: u8");
 7188        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7189        editor.signature_help_state = SignatureHelpState::default();
 7190    });
 7191
 7192    // Ensure that signature_help is called when auto signature help override is enabled
 7193    cx.update(|cx| {
 7194        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7195            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7196                settings.auto_signature_help = Some(true);
 7197                settings.show_signature_help_after_edits = Some(false);
 7198            });
 7199        });
 7200    });
 7201    cx.set_state(
 7202        &r#"
 7203            fn main() {
 7204                sampleˇ
 7205            }
 7206        "#
 7207        .unindent(),
 7208    );
 7209    cx.update_editor(|view, cx| {
 7210        view.handle_input("(", cx);
 7211    });
 7212    cx.assert_editor_state(
 7213        &"
 7214            fn main() {
 7215                sample(ˇ)
 7216            }
 7217        "
 7218        .unindent(),
 7219    );
 7220    handle_signature_help_request(&mut cx, mocked_response).await;
 7221    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7222        .await;
 7223    cx.editor(|editor, _| {
 7224        let signature_help_state = editor.signature_help_state.popover().cloned();
 7225        assert!(signature_help_state.is_some());
 7226        let ParsedMarkdown {
 7227            text, highlights, ..
 7228        } = signature_help_state.unwrap().parsed_content;
 7229        assert_eq!(text, "param1: u8, param2: u8");
 7230        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7231    });
 7232}
 7233
 7234#[gpui::test]
 7235async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7236    init_test(cx, |_| {});
 7237    cx.update(|cx| {
 7238        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7239            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7240                settings.auto_signature_help = Some(true);
 7241            });
 7242        });
 7243    });
 7244
 7245    let mut cx = EditorLspTestContext::new_rust(
 7246        lsp::ServerCapabilities {
 7247            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7248                ..Default::default()
 7249            }),
 7250            ..Default::default()
 7251        },
 7252        cx,
 7253    )
 7254    .await;
 7255
 7256    // A test that directly calls `show_signature_help`
 7257    cx.update_editor(|editor, cx| {
 7258        editor.show_signature_help(&ShowSignatureHelp, cx);
 7259    });
 7260
 7261    let mocked_response = lsp::SignatureHelp {
 7262        signatures: vec![lsp::SignatureInformation {
 7263            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7264            documentation: None,
 7265            parameters: Some(vec![
 7266                lsp::ParameterInformation {
 7267                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7268                    documentation: None,
 7269                },
 7270                lsp::ParameterInformation {
 7271                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7272                    documentation: None,
 7273                },
 7274            ]),
 7275            active_parameter: None,
 7276        }],
 7277        active_signature: Some(0),
 7278        active_parameter: Some(0),
 7279    };
 7280    handle_signature_help_request(&mut cx, mocked_response).await;
 7281
 7282    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7283        .await;
 7284
 7285    cx.editor(|editor, _| {
 7286        let signature_help_state = editor.signature_help_state.popover().cloned();
 7287        assert!(signature_help_state.is_some());
 7288        let ParsedMarkdown {
 7289            text, highlights, ..
 7290        } = signature_help_state.unwrap().parsed_content;
 7291        assert_eq!(text, "param1: u8, param2: u8");
 7292        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7293    });
 7294
 7295    // When exiting outside from inside the brackets, `signature_help` is closed.
 7296    cx.set_state(indoc! {"
 7297        fn main() {
 7298            sample(ˇ);
 7299        }
 7300
 7301        fn sample(param1: u8, param2: u8) {}
 7302    "});
 7303
 7304    cx.update_editor(|editor, cx| {
 7305        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7306    });
 7307
 7308    let mocked_response = lsp::SignatureHelp {
 7309        signatures: Vec::new(),
 7310        active_signature: None,
 7311        active_parameter: None,
 7312    };
 7313    handle_signature_help_request(&mut cx, mocked_response).await;
 7314
 7315    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7316        .await;
 7317
 7318    cx.editor(|editor, _| {
 7319        assert!(!editor.signature_help_state.is_shown());
 7320    });
 7321
 7322    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7323    cx.set_state(indoc! {"
 7324        fn main() {
 7325            sample(ˇ);
 7326        }
 7327
 7328        fn sample(param1: u8, param2: u8) {}
 7329    "});
 7330
 7331    let mocked_response = lsp::SignatureHelp {
 7332        signatures: vec![lsp::SignatureInformation {
 7333            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7334            documentation: None,
 7335            parameters: Some(vec![
 7336                lsp::ParameterInformation {
 7337                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7338                    documentation: None,
 7339                },
 7340                lsp::ParameterInformation {
 7341                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7342                    documentation: None,
 7343                },
 7344            ]),
 7345            active_parameter: None,
 7346        }],
 7347        active_signature: Some(0),
 7348        active_parameter: Some(0),
 7349    };
 7350    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7351    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7352        .await;
 7353    cx.editor(|editor, _| {
 7354        assert!(editor.signature_help_state.is_shown());
 7355    });
 7356
 7357    // Restore the popover with more parameter input
 7358    cx.set_state(indoc! {"
 7359        fn main() {
 7360            sample(param1, param2ˇ);
 7361        }
 7362
 7363        fn sample(param1: u8, param2: u8) {}
 7364    "});
 7365
 7366    let mocked_response = lsp::SignatureHelp {
 7367        signatures: vec![lsp::SignatureInformation {
 7368            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7369            documentation: None,
 7370            parameters: Some(vec![
 7371                lsp::ParameterInformation {
 7372                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7373                    documentation: None,
 7374                },
 7375                lsp::ParameterInformation {
 7376                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7377                    documentation: None,
 7378                },
 7379            ]),
 7380            active_parameter: None,
 7381        }],
 7382        active_signature: Some(0),
 7383        active_parameter: Some(1),
 7384    };
 7385    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7386    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7387        .await;
 7388
 7389    // When selecting a range, the popover is gone.
 7390    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7391    cx.update_editor(|editor, cx| {
 7392        editor.change_selections(None, cx, |s| {
 7393            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7394        })
 7395    });
 7396    cx.assert_editor_state(indoc! {"
 7397        fn main() {
 7398            sample(param1, «ˇparam2»);
 7399        }
 7400
 7401        fn sample(param1: u8, param2: u8) {}
 7402    "});
 7403    cx.editor(|editor, _| {
 7404        assert!(!editor.signature_help_state.is_shown());
 7405    });
 7406
 7407    // When unselecting again, the popover is back if within the brackets.
 7408    cx.update_editor(|editor, cx| {
 7409        editor.change_selections(None, cx, |s| {
 7410            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7411        })
 7412    });
 7413    cx.assert_editor_state(indoc! {"
 7414        fn main() {
 7415            sample(param1, ˇparam2);
 7416        }
 7417
 7418        fn sample(param1: u8, param2: u8) {}
 7419    "});
 7420    handle_signature_help_request(&mut cx, mocked_response).await;
 7421    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7422        .await;
 7423    cx.editor(|editor, _| {
 7424        assert!(editor.signature_help_state.is_shown());
 7425    });
 7426
 7427    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7428    cx.update_editor(|editor, cx| {
 7429        editor.change_selections(None, cx, |s| {
 7430            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7431            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7432        })
 7433    });
 7434    cx.assert_editor_state(indoc! {"
 7435        fn main() {
 7436            sample(param1, ˇparam2);
 7437        }
 7438
 7439        fn sample(param1: u8, param2: u8) {}
 7440    "});
 7441
 7442    let mocked_response = lsp::SignatureHelp {
 7443        signatures: vec![lsp::SignatureInformation {
 7444            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7445            documentation: None,
 7446            parameters: Some(vec![
 7447                lsp::ParameterInformation {
 7448                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7449                    documentation: None,
 7450                },
 7451                lsp::ParameterInformation {
 7452                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7453                    documentation: None,
 7454                },
 7455            ]),
 7456            active_parameter: None,
 7457        }],
 7458        active_signature: Some(0),
 7459        active_parameter: Some(1),
 7460    };
 7461    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7462    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7463        .await;
 7464    cx.update_editor(|editor, cx| {
 7465        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7466    });
 7467    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7468        .await;
 7469    cx.update_editor(|editor, cx| {
 7470        editor.change_selections(None, cx, |s| {
 7471            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7472        })
 7473    });
 7474    cx.assert_editor_state(indoc! {"
 7475        fn main() {
 7476            sample(param1, «ˇparam2»);
 7477        }
 7478
 7479        fn sample(param1: u8, param2: u8) {}
 7480    "});
 7481    cx.update_editor(|editor, cx| {
 7482        editor.change_selections(None, cx, |s| {
 7483            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7484        })
 7485    });
 7486    cx.assert_editor_state(indoc! {"
 7487        fn main() {
 7488            sample(param1, ˇparam2);
 7489        }
 7490
 7491        fn sample(param1: u8, param2: u8) {}
 7492    "});
 7493    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7494        .await;
 7495}
 7496
 7497#[gpui::test]
 7498async fn test_completion(cx: &mut gpui::TestAppContext) {
 7499    init_test(cx, |_| {});
 7500
 7501    let mut cx = EditorLspTestContext::new_rust(
 7502        lsp::ServerCapabilities {
 7503            completion_provider: Some(lsp::CompletionOptions {
 7504                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7505                resolve_provider: Some(true),
 7506                ..Default::default()
 7507            }),
 7508            ..Default::default()
 7509        },
 7510        cx,
 7511    )
 7512    .await;
 7513    let counter = Arc::new(AtomicUsize::new(0));
 7514
 7515    cx.set_state(indoc! {"
 7516        oneˇ
 7517        two
 7518        three
 7519    "});
 7520    cx.simulate_keystroke(".");
 7521    handle_completion_request(
 7522        &mut cx,
 7523        indoc! {"
 7524            one.|<>
 7525            two
 7526            three
 7527        "},
 7528        vec!["first_completion", "second_completion"],
 7529        counter.clone(),
 7530    )
 7531    .await;
 7532    cx.condition(|editor, _| editor.context_menu_visible())
 7533        .await;
 7534    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7535
 7536    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7537        editor.context_menu_next(&Default::default(), cx);
 7538        editor
 7539            .confirm_completion(&ConfirmCompletion::default(), cx)
 7540            .unwrap()
 7541    });
 7542    cx.assert_editor_state(indoc! {"
 7543        one.second_completionˇ
 7544        two
 7545        three
 7546    "});
 7547
 7548    handle_resolve_completion_request(
 7549        &mut cx,
 7550        Some(vec![
 7551            (
 7552                //This overlaps with the primary completion edit which is
 7553                //misbehavior from the LSP spec, test that we filter it out
 7554                indoc! {"
 7555                    one.second_ˇcompletion
 7556                    two
 7557                    threeˇ
 7558                "},
 7559                "overlapping additional edit",
 7560            ),
 7561            (
 7562                indoc! {"
 7563                    one.second_completion
 7564                    two
 7565                    threeˇ
 7566                "},
 7567                "\nadditional edit",
 7568            ),
 7569        ]),
 7570    )
 7571    .await;
 7572    apply_additional_edits.await.unwrap();
 7573    cx.assert_editor_state(indoc! {"
 7574        one.second_completionˇ
 7575        two
 7576        three
 7577        additional edit
 7578    "});
 7579
 7580    cx.set_state(indoc! {"
 7581        one.second_completion
 7582        twoˇ
 7583        threeˇ
 7584        additional edit
 7585    "});
 7586    cx.simulate_keystroke(" ");
 7587    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7588    cx.simulate_keystroke("s");
 7589    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7590
 7591    cx.assert_editor_state(indoc! {"
 7592        one.second_completion
 7593        two sˇ
 7594        three sˇ
 7595        additional edit
 7596    "});
 7597    handle_completion_request(
 7598        &mut cx,
 7599        indoc! {"
 7600            one.second_completion
 7601            two s
 7602            three <s|>
 7603            additional edit
 7604        "},
 7605        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7606        counter.clone(),
 7607    )
 7608    .await;
 7609    cx.condition(|editor, _| editor.context_menu_visible())
 7610        .await;
 7611    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7612
 7613    cx.simulate_keystroke("i");
 7614
 7615    handle_completion_request(
 7616        &mut cx,
 7617        indoc! {"
 7618            one.second_completion
 7619            two si
 7620            three <si|>
 7621            additional edit
 7622        "},
 7623        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7624        counter.clone(),
 7625    )
 7626    .await;
 7627    cx.condition(|editor, _| editor.context_menu_visible())
 7628        .await;
 7629    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7630
 7631    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7632        editor
 7633            .confirm_completion(&ConfirmCompletion::default(), cx)
 7634            .unwrap()
 7635    });
 7636    cx.assert_editor_state(indoc! {"
 7637        one.second_completion
 7638        two sixth_completionˇ
 7639        three sixth_completionˇ
 7640        additional edit
 7641    "});
 7642
 7643    handle_resolve_completion_request(&mut cx, None).await;
 7644    apply_additional_edits.await.unwrap();
 7645
 7646    _ = cx.update(|cx| {
 7647        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7648            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7649                settings.show_completions_on_input = Some(false);
 7650            });
 7651        })
 7652    });
 7653    cx.set_state("editorˇ");
 7654    cx.simulate_keystroke(".");
 7655    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7656    cx.simulate_keystroke("c");
 7657    cx.simulate_keystroke("l");
 7658    cx.simulate_keystroke("o");
 7659    cx.assert_editor_state("editor.cloˇ");
 7660    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7661    cx.update_editor(|editor, cx| {
 7662        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7663    });
 7664    handle_completion_request(
 7665        &mut cx,
 7666        "editor.<clo|>",
 7667        vec!["close", "clobber"],
 7668        counter.clone(),
 7669    )
 7670    .await;
 7671    cx.condition(|editor, _| editor.context_menu_visible())
 7672        .await;
 7673    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7674
 7675    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7676        editor
 7677            .confirm_completion(&ConfirmCompletion::default(), cx)
 7678            .unwrap()
 7679    });
 7680    cx.assert_editor_state("editor.closeˇ");
 7681    handle_resolve_completion_request(&mut cx, None).await;
 7682    apply_additional_edits.await.unwrap();
 7683}
 7684
 7685#[gpui::test]
 7686async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7687    init_test(cx, |_| {});
 7688    let mut cx = EditorLspTestContext::new_rust(
 7689        lsp::ServerCapabilities {
 7690            completion_provider: Some(lsp::CompletionOptions {
 7691                trigger_characters: Some(vec![".".to_string()]),
 7692                ..Default::default()
 7693            }),
 7694            ..Default::default()
 7695        },
 7696        cx,
 7697    )
 7698    .await;
 7699    cx.lsp
 7700        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7701            Ok(Some(lsp::CompletionResponse::Array(vec![
 7702                lsp::CompletionItem {
 7703                    label: "first".into(),
 7704                    ..Default::default()
 7705                },
 7706                lsp::CompletionItem {
 7707                    label: "last".into(),
 7708                    ..Default::default()
 7709                },
 7710            ])))
 7711        });
 7712    cx.set_state("variableˇ");
 7713    cx.simulate_keystroke(".");
 7714    cx.executor().run_until_parked();
 7715
 7716    cx.update_editor(|editor, _| {
 7717        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7718            assert_eq!(
 7719                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7720                &["first", "last"]
 7721            );
 7722        } else {
 7723            panic!("expected completion menu to be open");
 7724        }
 7725    });
 7726
 7727    cx.update_editor(|editor, cx| {
 7728        editor.move_page_down(&MovePageDown::default(), cx);
 7729        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7730            assert!(
 7731                menu.selected_item == 1,
 7732                "expected PageDown to select the last item from the context menu"
 7733            );
 7734        } else {
 7735            panic!("expected completion menu to stay open after PageDown");
 7736        }
 7737    });
 7738
 7739    cx.update_editor(|editor, cx| {
 7740        editor.move_page_up(&MovePageUp::default(), cx);
 7741        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7742            assert!(
 7743                menu.selected_item == 0,
 7744                "expected PageUp to select the first item from the context menu"
 7745            );
 7746        } else {
 7747            panic!("expected completion menu to stay open after PageUp");
 7748        }
 7749    });
 7750}
 7751
 7752#[gpui::test]
 7753async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7754    init_test(cx, |_| {});
 7755
 7756    let mut cx = EditorLspTestContext::new_rust(
 7757        lsp::ServerCapabilities {
 7758            completion_provider: Some(lsp::CompletionOptions {
 7759                trigger_characters: Some(vec![".".to_string()]),
 7760                resolve_provider: Some(true),
 7761                ..Default::default()
 7762            }),
 7763            ..Default::default()
 7764        },
 7765        cx,
 7766    )
 7767    .await;
 7768
 7769    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7770    cx.simulate_keystroke(".");
 7771    let completion_item = lsp::CompletionItem {
 7772        label: "Some".into(),
 7773        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7774        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7775        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7776            kind: lsp::MarkupKind::Markdown,
 7777            value: "```rust\nSome(2)\n```".to_string(),
 7778        })),
 7779        deprecated: Some(false),
 7780        sort_text: Some("Some".to_string()),
 7781        filter_text: Some("Some".to_string()),
 7782        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7783        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7784            range: lsp::Range {
 7785                start: lsp::Position {
 7786                    line: 0,
 7787                    character: 22,
 7788                },
 7789                end: lsp::Position {
 7790                    line: 0,
 7791                    character: 22,
 7792                },
 7793            },
 7794            new_text: "Some(2)".to_string(),
 7795        })),
 7796        additional_text_edits: Some(vec![lsp::TextEdit {
 7797            range: lsp::Range {
 7798                start: lsp::Position {
 7799                    line: 0,
 7800                    character: 20,
 7801                },
 7802                end: lsp::Position {
 7803                    line: 0,
 7804                    character: 22,
 7805                },
 7806            },
 7807            new_text: "".to_string(),
 7808        }]),
 7809        ..Default::default()
 7810    };
 7811
 7812    let closure_completion_item = completion_item.clone();
 7813    let counter = Arc::new(AtomicUsize::new(0));
 7814    let counter_clone = counter.clone();
 7815    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7816        let task_completion_item = closure_completion_item.clone();
 7817        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7818        async move {
 7819            Ok(Some(lsp::CompletionResponse::Array(vec![
 7820                task_completion_item,
 7821            ])))
 7822        }
 7823    });
 7824
 7825    cx.condition(|editor, _| editor.context_menu_visible())
 7826        .await;
 7827    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7828    assert!(request.next().await.is_some());
 7829    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7830
 7831    cx.simulate_keystroke("S");
 7832    cx.simulate_keystroke("o");
 7833    cx.simulate_keystroke("m");
 7834    cx.condition(|editor, _| editor.context_menu_visible())
 7835        .await;
 7836    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7837    assert!(request.next().await.is_some());
 7838    assert!(request.next().await.is_some());
 7839    assert!(request.next().await.is_some());
 7840    request.close();
 7841    assert!(request.next().await.is_none());
 7842    assert_eq!(
 7843        counter.load(atomic::Ordering::Acquire),
 7844        4,
 7845        "With the completions menu open, only one LSP request should happen per input"
 7846    );
 7847}
 7848
 7849#[gpui::test]
 7850async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7851    init_test(cx, |_| {});
 7852    let mut cx = EditorTestContext::new(cx).await;
 7853    let language = Arc::new(Language::new(
 7854        LanguageConfig {
 7855            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7856            ..Default::default()
 7857        },
 7858        Some(tree_sitter_rust::language()),
 7859    ));
 7860    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7861
 7862    // If multiple selections intersect a line, the line is only toggled once.
 7863    cx.set_state(indoc! {"
 7864        fn a() {
 7865            «//b();
 7866            ˇ»// «c();
 7867            //ˇ»  d();
 7868        }
 7869    "});
 7870
 7871    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7872
 7873    cx.assert_editor_state(indoc! {"
 7874        fn a() {
 7875            «b();
 7876            c();
 7877            ˇ» d();
 7878        }
 7879    "});
 7880
 7881    // The comment prefix is inserted at the same column for every line in a
 7882    // selection.
 7883    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7884
 7885    cx.assert_editor_state(indoc! {"
 7886        fn a() {
 7887            // «b();
 7888            // c();
 7889            ˇ»//  d();
 7890        }
 7891    "});
 7892
 7893    // If a selection ends at the beginning of a line, that line is not toggled.
 7894    cx.set_selections_state(indoc! {"
 7895        fn a() {
 7896            // b();
 7897            «// c();
 7898        ˇ»    //  d();
 7899        }
 7900    "});
 7901
 7902    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7903
 7904    cx.assert_editor_state(indoc! {"
 7905        fn a() {
 7906            // b();
 7907            «c();
 7908        ˇ»    //  d();
 7909        }
 7910    "});
 7911
 7912    // If a selection span a single line and is empty, the line is toggled.
 7913    cx.set_state(indoc! {"
 7914        fn a() {
 7915            a();
 7916            b();
 7917        ˇ
 7918        }
 7919    "});
 7920
 7921    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7922
 7923    cx.assert_editor_state(indoc! {"
 7924        fn a() {
 7925            a();
 7926            b();
 7927        //•ˇ
 7928        }
 7929    "});
 7930
 7931    // If a selection span multiple lines, empty lines are not toggled.
 7932    cx.set_state(indoc! {"
 7933        fn a() {
 7934            «a();
 7935
 7936            c();ˇ»
 7937        }
 7938    "});
 7939
 7940    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7941
 7942    cx.assert_editor_state(indoc! {"
 7943        fn a() {
 7944            // «a();
 7945
 7946            // c();ˇ»
 7947        }
 7948    "});
 7949
 7950    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7951    cx.set_state(indoc! {"
 7952        fn a() {
 7953            «// a();
 7954            /// b();
 7955            //! c();ˇ»
 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            «a();
 7964            b();
 7965            c();ˇ»
 7966        }
 7967    "});
 7968}
 7969
 7970#[gpui::test]
 7971async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7972    init_test(cx, |_| {});
 7973
 7974    let language = Arc::new(Language::new(
 7975        LanguageConfig {
 7976            line_comments: vec!["// ".into()],
 7977            ..Default::default()
 7978        },
 7979        Some(tree_sitter_rust::language()),
 7980    ));
 7981
 7982    let mut cx = EditorTestContext::new(cx).await;
 7983
 7984    cx.language_registry().add(language.clone());
 7985    cx.update_buffer(|buffer, cx| {
 7986        buffer.set_language(Some(language), cx);
 7987    });
 7988
 7989    let toggle_comments = &ToggleComments {
 7990        advance_downwards: true,
 7991    };
 7992
 7993    // Single cursor on one line -> advance
 7994    // Cursor moves horizontally 3 characters as well on non-blank line
 7995    cx.set_state(indoc!(
 7996        "fn a() {
 7997             ˇdog();
 7998             cat();
 7999        }"
 8000    ));
 8001    cx.update_editor(|editor, cx| {
 8002        editor.toggle_comments(toggle_comments, cx);
 8003    });
 8004    cx.assert_editor_state(indoc!(
 8005        "fn a() {
 8006             // dog();
 8007             catˇ();
 8008        }"
 8009    ));
 8010
 8011    // Single selection on one line -> don't advance
 8012    cx.set_state(indoc!(
 8013        "fn a() {
 8014             «dog()ˇ»;
 8015             cat();
 8016        }"
 8017    ));
 8018    cx.update_editor(|editor, cx| {
 8019        editor.toggle_comments(toggle_comments, cx);
 8020    });
 8021    cx.assert_editor_state(indoc!(
 8022        "fn a() {
 8023             // «dog()ˇ»;
 8024             cat();
 8025        }"
 8026    ));
 8027
 8028    // Multiple cursors on one line -> advance
 8029    cx.set_state(indoc!(
 8030        "fn a() {
 8031             ˇdˇog();
 8032             cat();
 8033        }"
 8034    ));
 8035    cx.update_editor(|editor, cx| {
 8036        editor.toggle_comments(toggle_comments, cx);
 8037    });
 8038    cx.assert_editor_state(indoc!(
 8039        "fn a() {
 8040             // dog();
 8041             catˇ(ˇ);
 8042        }"
 8043    ));
 8044
 8045    // Multiple cursors on one line, with selection -> don't advance
 8046    cx.set_state(indoc!(
 8047        "fn a() {
 8048             ˇdˇog«()ˇ»;
 8049             cat();
 8050        }"
 8051    ));
 8052    cx.update_editor(|editor, cx| {
 8053        editor.toggle_comments(toggle_comments, cx);
 8054    });
 8055    cx.assert_editor_state(indoc!(
 8056        "fn a() {
 8057             // ˇdˇog«()ˇ»;
 8058             cat();
 8059        }"
 8060    ));
 8061
 8062    // Single cursor on one line -> advance
 8063    // Cursor moves to column 0 on blank line
 8064    cx.set_state(indoc!(
 8065        "fn a() {
 8066             ˇdog();
 8067
 8068             cat();
 8069        }"
 8070    ));
 8071    cx.update_editor(|editor, cx| {
 8072        editor.toggle_comments(toggle_comments, cx);
 8073    });
 8074    cx.assert_editor_state(indoc!(
 8075        "fn a() {
 8076             // dog();
 8077        ˇ
 8078             cat();
 8079        }"
 8080    ));
 8081
 8082    // Single cursor on one line -> advance
 8083    // Cursor starts and ends at column 0
 8084    cx.set_state(indoc!(
 8085        "fn a() {
 8086         ˇ    dog();
 8087             cat();
 8088        }"
 8089    ));
 8090    cx.update_editor(|editor, cx| {
 8091        editor.toggle_comments(toggle_comments, cx);
 8092    });
 8093    cx.assert_editor_state(indoc!(
 8094        "fn a() {
 8095             // dog();
 8096         ˇ    cat();
 8097        }"
 8098    ));
 8099}
 8100
 8101#[gpui::test]
 8102async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8103    init_test(cx, |_| {});
 8104
 8105    let mut cx = EditorTestContext::new(cx).await;
 8106
 8107    let html_language = Arc::new(
 8108        Language::new(
 8109            LanguageConfig {
 8110                name: "HTML".into(),
 8111                block_comment: Some(("<!-- ".into(), " -->".into())),
 8112                ..Default::default()
 8113            },
 8114            Some(tree_sitter_html::language()),
 8115        )
 8116        .with_injection_query(
 8117            r#"
 8118            (script_element
 8119                (raw_text) @content
 8120                (#set! "language" "javascript"))
 8121            "#,
 8122        )
 8123        .unwrap(),
 8124    );
 8125
 8126    let javascript_language = Arc::new(Language::new(
 8127        LanguageConfig {
 8128            name: "JavaScript".into(),
 8129            line_comments: vec!["// ".into()],
 8130            ..Default::default()
 8131        },
 8132        Some(tree_sitter_typescript::language_tsx()),
 8133    ));
 8134
 8135    cx.language_registry().add(html_language.clone());
 8136    cx.language_registry().add(javascript_language.clone());
 8137    cx.update_buffer(|buffer, cx| {
 8138        buffer.set_language(Some(html_language), cx);
 8139    });
 8140
 8141    // Toggle comments for empty selections
 8142    cx.set_state(
 8143        &r#"
 8144            <p>A</p>ˇ
 8145            <p>B</p>ˇ
 8146            <p>C</p>ˇ
 8147        "#
 8148        .unindent(),
 8149    );
 8150    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8151    cx.assert_editor_state(
 8152        &r#"
 8153            <!-- <p>A</p>ˇ -->
 8154            <!-- <p>B</p>ˇ -->
 8155            <!-- <p>C</p>ˇ -->
 8156        "#
 8157        .unindent(),
 8158    );
 8159    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8160    cx.assert_editor_state(
 8161        &r#"
 8162            <p>A</p>ˇ
 8163            <p>B</p>ˇ
 8164            <p>C</p>ˇ
 8165        "#
 8166        .unindent(),
 8167    );
 8168
 8169    // Toggle comments for mixture of empty and non-empty selections, where
 8170    // multiple selections occupy a given line.
 8171    cx.set_state(
 8172        &r#"
 8173            <p>A«</p>
 8174            <p>ˇ»B</p>ˇ
 8175            <p>C«</p>
 8176            <p>ˇ»D</p>ˇ
 8177        "#
 8178        .unindent(),
 8179    );
 8180
 8181    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8182    cx.assert_editor_state(
 8183        &r#"
 8184            <!-- <p>A«</p>
 8185            <p>ˇ»B</p>ˇ -->
 8186            <!-- <p>C«</p>
 8187            <p>ˇ»D</p>ˇ -->
 8188        "#
 8189        .unindent(),
 8190    );
 8191    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8192    cx.assert_editor_state(
 8193        &r#"
 8194            <p>A«</p>
 8195            <p>ˇ»B</p>ˇ
 8196            <p>C«</p>
 8197            <p>ˇ»D</p>ˇ
 8198        "#
 8199        .unindent(),
 8200    );
 8201
 8202    // Toggle comments when different languages are active for different
 8203    // selections.
 8204    cx.set_state(
 8205        &r#"
 8206            ˇ<script>
 8207                ˇvar x = new Y();
 8208            ˇ</script>
 8209        "#
 8210        .unindent(),
 8211    );
 8212    cx.executor().run_until_parked();
 8213    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8214    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8215    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8216    cx.assert_editor_state(
 8217        &r#"
 8218            <!-- ˇ<script> -->
 8219                // ˇvar x = new Y();
 8220            // ˇ</script>
 8221        "#
 8222        .unindent(),
 8223    );
 8224}
 8225
 8226#[gpui::test]
 8227fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8228    init_test(cx, |_| {});
 8229
 8230    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8231    let multibuffer = cx.new_model(|cx| {
 8232        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8233        multibuffer.push_excerpts(
 8234            buffer.clone(),
 8235            [
 8236                ExcerptRange {
 8237                    context: Point::new(0, 0)..Point::new(0, 4),
 8238                    primary: None,
 8239                },
 8240                ExcerptRange {
 8241                    context: Point::new(1, 0)..Point::new(1, 4),
 8242                    primary: None,
 8243                },
 8244            ],
 8245            cx,
 8246        );
 8247        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8248        multibuffer
 8249    });
 8250
 8251    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8252    _ = view.update(cx, |view, cx| {
 8253        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8254        view.change_selections(None, cx, |s| {
 8255            s.select_ranges([
 8256                Point::new(0, 0)..Point::new(0, 0),
 8257                Point::new(1, 0)..Point::new(1, 0),
 8258            ])
 8259        });
 8260
 8261        view.handle_input("X", cx);
 8262        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8263        assert_eq!(
 8264            view.selections.ranges(cx),
 8265            [
 8266                Point::new(0, 1)..Point::new(0, 1),
 8267                Point::new(1, 1)..Point::new(1, 1),
 8268            ]
 8269        );
 8270
 8271        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8272        view.change_selections(None, cx, |s| {
 8273            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8274        });
 8275        view.backspace(&Default::default(), cx);
 8276        assert_eq!(view.text(cx), "Xa\nbbb");
 8277        assert_eq!(
 8278            view.selections.ranges(cx),
 8279            [Point::new(1, 0)..Point::new(1, 0)]
 8280        );
 8281
 8282        view.change_selections(None, cx, |s| {
 8283            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8284        });
 8285        view.backspace(&Default::default(), cx);
 8286        assert_eq!(view.text(cx), "X\nbb");
 8287        assert_eq!(
 8288            view.selections.ranges(cx),
 8289            [Point::new(0, 1)..Point::new(0, 1)]
 8290        );
 8291    });
 8292}
 8293
 8294#[gpui::test]
 8295fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8296    init_test(cx, |_| {});
 8297
 8298    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8299    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8300        indoc! {"
 8301            [aaaa
 8302            (bbbb]
 8303            cccc)",
 8304        },
 8305        markers.clone(),
 8306    );
 8307    let excerpt_ranges = markers.into_iter().map(|marker| {
 8308        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8309        ExcerptRange {
 8310            context,
 8311            primary: None,
 8312        }
 8313    });
 8314    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8315    let multibuffer = cx.new_model(|cx| {
 8316        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8317        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8318        multibuffer
 8319    });
 8320
 8321    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8322    _ = view.update(cx, |view, cx| {
 8323        let (expected_text, selection_ranges) = marked_text_ranges(
 8324            indoc! {"
 8325                aaaa
 8326                bˇbbb
 8327                bˇbbˇb
 8328                cccc"
 8329            },
 8330            true,
 8331        );
 8332        assert_eq!(view.text(cx), expected_text);
 8333        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8334
 8335        view.handle_input("X", cx);
 8336
 8337        let (expected_text, expected_selections) = marked_text_ranges(
 8338            indoc! {"
 8339                aaaa
 8340                bXˇbbXb
 8341                bXˇbbXˇb
 8342                cccc"
 8343            },
 8344            false,
 8345        );
 8346        assert_eq!(view.text(cx), expected_text);
 8347        assert_eq!(view.selections.ranges(cx), expected_selections);
 8348
 8349        view.newline(&Newline, cx);
 8350        let (expected_text, expected_selections) = marked_text_ranges(
 8351            indoc! {"
 8352                aaaa
 8353                bX
 8354                ˇbbX
 8355                b
 8356                bX
 8357                ˇbbX
 8358                ˇb
 8359                cccc"
 8360            },
 8361            false,
 8362        );
 8363        assert_eq!(view.text(cx), expected_text);
 8364        assert_eq!(view.selections.ranges(cx), expected_selections);
 8365    });
 8366}
 8367
 8368#[gpui::test]
 8369fn test_refresh_selections(cx: &mut TestAppContext) {
 8370    init_test(cx, |_| {});
 8371
 8372    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8373    let mut excerpt1_id = None;
 8374    let multibuffer = cx.new_model(|cx| {
 8375        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8376        excerpt1_id = multibuffer
 8377            .push_excerpts(
 8378                buffer.clone(),
 8379                [
 8380                    ExcerptRange {
 8381                        context: Point::new(0, 0)..Point::new(1, 4),
 8382                        primary: None,
 8383                    },
 8384                    ExcerptRange {
 8385                        context: Point::new(1, 0)..Point::new(2, 4),
 8386                        primary: None,
 8387                    },
 8388                ],
 8389                cx,
 8390            )
 8391            .into_iter()
 8392            .next();
 8393        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8394        multibuffer
 8395    });
 8396
 8397    let editor = cx.add_window(|cx| {
 8398        let mut editor = build_editor(multibuffer.clone(), cx);
 8399        let snapshot = editor.snapshot(cx);
 8400        editor.change_selections(None, cx, |s| {
 8401            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8402        });
 8403        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8404        assert_eq!(
 8405            editor.selections.ranges(cx),
 8406            [
 8407                Point::new(1, 3)..Point::new(1, 3),
 8408                Point::new(2, 1)..Point::new(2, 1),
 8409            ]
 8410        );
 8411        editor
 8412    });
 8413
 8414    // Refreshing selections is a no-op when excerpts haven't changed.
 8415    _ = editor.update(cx, |editor, cx| {
 8416        editor.change_selections(None, cx, |s| s.refresh());
 8417        assert_eq!(
 8418            editor.selections.ranges(cx),
 8419            [
 8420                Point::new(1, 3)..Point::new(1, 3),
 8421                Point::new(2, 1)..Point::new(2, 1),
 8422            ]
 8423        );
 8424    });
 8425
 8426    _ = multibuffer.update(cx, |multibuffer, cx| {
 8427        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8428    });
 8429    _ = editor.update(cx, |editor, cx| {
 8430        // Removing an excerpt causes the first selection to become degenerate.
 8431        assert_eq!(
 8432            editor.selections.ranges(cx),
 8433            [
 8434                Point::new(0, 0)..Point::new(0, 0),
 8435                Point::new(0, 1)..Point::new(0, 1)
 8436            ]
 8437        );
 8438
 8439        // Refreshing selections will relocate the first selection to the original buffer
 8440        // location.
 8441        editor.change_selections(None, cx, |s| s.refresh());
 8442        assert_eq!(
 8443            editor.selections.ranges(cx),
 8444            [
 8445                Point::new(0, 1)..Point::new(0, 1),
 8446                Point::new(0, 3)..Point::new(0, 3)
 8447            ]
 8448        );
 8449        assert!(editor.selections.pending_anchor().is_some());
 8450    });
 8451}
 8452
 8453#[gpui::test]
 8454fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8455    init_test(cx, |_| {});
 8456
 8457    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8458    let mut excerpt1_id = None;
 8459    let multibuffer = cx.new_model(|cx| {
 8460        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8461        excerpt1_id = multibuffer
 8462            .push_excerpts(
 8463                buffer.clone(),
 8464                [
 8465                    ExcerptRange {
 8466                        context: Point::new(0, 0)..Point::new(1, 4),
 8467                        primary: None,
 8468                    },
 8469                    ExcerptRange {
 8470                        context: Point::new(1, 0)..Point::new(2, 4),
 8471                        primary: None,
 8472                    },
 8473                ],
 8474                cx,
 8475            )
 8476            .into_iter()
 8477            .next();
 8478        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8479        multibuffer
 8480    });
 8481
 8482    let editor = cx.add_window(|cx| {
 8483        let mut editor = build_editor(multibuffer.clone(), cx);
 8484        let snapshot = editor.snapshot(cx);
 8485        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8486        assert_eq!(
 8487            editor.selections.ranges(cx),
 8488            [Point::new(1, 3)..Point::new(1, 3)]
 8489        );
 8490        editor
 8491    });
 8492
 8493    _ = multibuffer.update(cx, |multibuffer, cx| {
 8494        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8495    });
 8496    _ = editor.update(cx, |editor, cx| {
 8497        assert_eq!(
 8498            editor.selections.ranges(cx),
 8499            [Point::new(0, 0)..Point::new(0, 0)]
 8500        );
 8501
 8502        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8503        editor.change_selections(None, cx, |s| s.refresh());
 8504        assert_eq!(
 8505            editor.selections.ranges(cx),
 8506            [Point::new(0, 3)..Point::new(0, 3)]
 8507        );
 8508        assert!(editor.selections.pending_anchor().is_some());
 8509    });
 8510}
 8511
 8512#[gpui::test]
 8513async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8514    init_test(cx, |_| {});
 8515
 8516    let language = Arc::new(
 8517        Language::new(
 8518            LanguageConfig {
 8519                brackets: BracketPairConfig {
 8520                    pairs: vec![
 8521                        BracketPair {
 8522                            start: "{".to_string(),
 8523                            end: "}".to_string(),
 8524                            close: true,
 8525                            surround: true,
 8526                            newline: true,
 8527                        },
 8528                        BracketPair {
 8529                            start: "/* ".to_string(),
 8530                            end: " */".to_string(),
 8531                            close: true,
 8532                            surround: true,
 8533                            newline: true,
 8534                        },
 8535                    ],
 8536                    ..Default::default()
 8537                },
 8538                ..Default::default()
 8539            },
 8540            Some(tree_sitter_rust::language()),
 8541        )
 8542        .with_indents_query("")
 8543        .unwrap(),
 8544    );
 8545
 8546    let text = concat!(
 8547        "{   }\n",     //
 8548        "  x\n",       //
 8549        "  /*   */\n", //
 8550        "x\n",         //
 8551        "{{} }\n",     //
 8552    );
 8553
 8554    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8555    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8556    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8557    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8558        .await;
 8559
 8560    _ = view.update(cx, |view, cx| {
 8561        view.change_selections(None, cx, |s| {
 8562            s.select_display_ranges([
 8563                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8564                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8565                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8566            ])
 8567        });
 8568        view.newline(&Newline, cx);
 8569
 8570        assert_eq!(
 8571            view.buffer().read(cx).read(cx).text(),
 8572            concat!(
 8573                "{ \n",    // Suppress rustfmt
 8574                "\n",      //
 8575                "}\n",     //
 8576                "  x\n",   //
 8577                "  /* \n", //
 8578                "  \n",    //
 8579                "  */\n",  //
 8580                "x\n",     //
 8581                "{{} \n",  //
 8582                "}\n",     //
 8583            )
 8584        );
 8585    });
 8586}
 8587
 8588#[gpui::test]
 8589fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8590    init_test(cx, |_| {});
 8591
 8592    let editor = cx.add_window(|cx| {
 8593        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8594        build_editor(buffer.clone(), cx)
 8595    });
 8596
 8597    _ = editor.update(cx, |editor, cx| {
 8598        struct Type1;
 8599        struct Type2;
 8600
 8601        let buffer = editor.buffer.read(cx).snapshot(cx);
 8602
 8603        let anchor_range =
 8604            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8605
 8606        editor.highlight_background::<Type1>(
 8607            &[
 8608                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8609                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8610                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8611                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8612            ],
 8613            |_| Hsla::red(),
 8614            cx,
 8615        );
 8616        editor.highlight_background::<Type2>(
 8617            &[
 8618                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8619                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8620                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8621                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8622            ],
 8623            |_| Hsla::green(),
 8624            cx,
 8625        );
 8626
 8627        let snapshot = editor.snapshot(cx);
 8628        let mut highlighted_ranges = editor.background_highlights_in_range(
 8629            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8630            &snapshot,
 8631            cx.theme().colors(),
 8632        );
 8633        // Enforce a consistent ordering based on color without relying on the ordering of the
 8634        // highlight's `TypeId` which is non-executor.
 8635        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8636        assert_eq!(
 8637            highlighted_ranges,
 8638            &[
 8639                (
 8640                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8641                    Hsla::red(),
 8642                ),
 8643                (
 8644                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8645                    Hsla::red(),
 8646                ),
 8647                (
 8648                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8649                    Hsla::green(),
 8650                ),
 8651                (
 8652                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8653                    Hsla::green(),
 8654                ),
 8655            ]
 8656        );
 8657        assert_eq!(
 8658            editor.background_highlights_in_range(
 8659                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8660                &snapshot,
 8661                cx.theme().colors(),
 8662            ),
 8663            &[(
 8664                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8665                Hsla::red(),
 8666            )]
 8667        );
 8668    });
 8669}
 8670
 8671#[gpui::test]
 8672async fn test_following(cx: &mut gpui::TestAppContext) {
 8673    init_test(cx, |_| {});
 8674
 8675    let fs = FakeFs::new(cx.executor());
 8676    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8677
 8678    let buffer = project.update(cx, |project, cx| {
 8679        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8680        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8681    });
 8682    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8683    let follower = cx.update(|cx| {
 8684        cx.open_window(
 8685            WindowOptions {
 8686                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8687                    gpui::Point::new(px(0.), px(0.)),
 8688                    gpui::Point::new(px(10.), px(80.)),
 8689                ))),
 8690                ..Default::default()
 8691            },
 8692            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8693        )
 8694        .unwrap()
 8695    });
 8696
 8697    let is_still_following = Rc::new(RefCell::new(true));
 8698    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8699    let pending_update = Rc::new(RefCell::new(None));
 8700    _ = follower.update(cx, {
 8701        let update = pending_update.clone();
 8702        let is_still_following = is_still_following.clone();
 8703        let follower_edit_event_count = follower_edit_event_count.clone();
 8704        |_, cx| {
 8705            cx.subscribe(
 8706                &leader.root_view(cx).unwrap(),
 8707                move |_, leader, event, cx| {
 8708                    leader
 8709                        .read(cx)
 8710                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8711                },
 8712            )
 8713            .detach();
 8714
 8715            cx.subscribe(
 8716                &follower.root_view(cx).unwrap(),
 8717                move |_, _, event: &EditorEvent, _cx| {
 8718                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8719                        *is_still_following.borrow_mut() = false;
 8720                    }
 8721
 8722                    if let EditorEvent::BufferEdited = event {
 8723                        *follower_edit_event_count.borrow_mut() += 1;
 8724                    }
 8725                },
 8726            )
 8727            .detach();
 8728        }
 8729    });
 8730
 8731    // Update the selections only
 8732    _ = leader.update(cx, |leader, cx| {
 8733        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8734    });
 8735    follower
 8736        .update(cx, |follower, cx| {
 8737            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8738        })
 8739        .unwrap()
 8740        .await
 8741        .unwrap();
 8742    _ = follower.update(cx, |follower, cx| {
 8743        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8744    });
 8745    assert_eq!(*is_still_following.borrow(), true);
 8746    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8747
 8748    // Update the scroll position only
 8749    _ = leader.update(cx, |leader, cx| {
 8750        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8751    });
 8752    follower
 8753        .update(cx, |follower, cx| {
 8754            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8755        })
 8756        .unwrap()
 8757        .await
 8758        .unwrap();
 8759    assert_eq!(
 8760        follower
 8761            .update(cx, |follower, cx| follower.scroll_position(cx))
 8762            .unwrap(),
 8763        gpui::Point::new(1.5, 3.5)
 8764    );
 8765    assert_eq!(*is_still_following.borrow(), true);
 8766    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8767
 8768    // Update the selections and scroll position. The follower's scroll position is updated
 8769    // via autoscroll, not via the leader's exact scroll position.
 8770    _ = leader.update(cx, |leader, cx| {
 8771        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8772        leader.request_autoscroll(Autoscroll::newest(), cx);
 8773        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8774    });
 8775    follower
 8776        .update(cx, |follower, cx| {
 8777            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8778        })
 8779        .unwrap()
 8780        .await
 8781        .unwrap();
 8782    _ = follower.update(cx, |follower, cx| {
 8783        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8784        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8785    });
 8786    assert_eq!(*is_still_following.borrow(), true);
 8787
 8788    // Creating a pending selection that precedes another selection
 8789    _ = leader.update(cx, |leader, cx| {
 8790        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8791        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8792    });
 8793    follower
 8794        .update(cx, |follower, cx| {
 8795            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8796        })
 8797        .unwrap()
 8798        .await
 8799        .unwrap();
 8800    _ = follower.update(cx, |follower, cx| {
 8801        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8802    });
 8803    assert_eq!(*is_still_following.borrow(), true);
 8804
 8805    // Extend the pending selection so that it surrounds another selection
 8806    _ = leader.update(cx, |leader, cx| {
 8807        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8808    });
 8809    follower
 8810        .update(cx, |follower, cx| {
 8811            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8812        })
 8813        .unwrap()
 8814        .await
 8815        .unwrap();
 8816    _ = follower.update(cx, |follower, cx| {
 8817        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8818    });
 8819
 8820    // Scrolling locally breaks the follow
 8821    _ = follower.update(cx, |follower, cx| {
 8822        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8823        follower.set_scroll_anchor(
 8824            ScrollAnchor {
 8825                anchor: top_anchor,
 8826                offset: gpui::Point::new(0.0, 0.5),
 8827            },
 8828            cx,
 8829        );
 8830    });
 8831    assert_eq!(*is_still_following.borrow(), false);
 8832}
 8833
 8834#[gpui::test]
 8835async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8836    init_test(cx, |_| {});
 8837
 8838    let fs = FakeFs::new(cx.executor());
 8839    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8840    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8841    let pane = workspace
 8842        .update(cx, |workspace, _| workspace.active_pane().clone())
 8843        .unwrap();
 8844
 8845    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8846
 8847    let leader = pane.update(cx, |_, cx| {
 8848        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8849        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8850    });
 8851
 8852    // Start following the editor when it has no excerpts.
 8853    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8854    let follower_1 = cx
 8855        .update_window(*workspace.deref(), |_, cx| {
 8856            Editor::from_state_proto(
 8857                workspace.root_view(cx).unwrap(),
 8858                ViewId {
 8859                    creator: Default::default(),
 8860                    id: 0,
 8861                },
 8862                &mut state_message,
 8863                cx,
 8864            )
 8865        })
 8866        .unwrap()
 8867        .unwrap()
 8868        .await
 8869        .unwrap();
 8870
 8871    let update_message = Rc::new(RefCell::new(None));
 8872    follower_1.update(cx, {
 8873        let update = update_message.clone();
 8874        |_, cx| {
 8875            cx.subscribe(&leader, move |_, leader, event, cx| {
 8876                leader
 8877                    .read(cx)
 8878                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8879            })
 8880            .detach();
 8881        }
 8882    });
 8883
 8884    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8885        (
 8886            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8887            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8888        )
 8889    });
 8890
 8891    // Insert some excerpts.
 8892    _ = leader.update(cx, |leader, cx| {
 8893        leader.buffer.update(cx, |multibuffer, cx| {
 8894            let excerpt_ids = multibuffer.push_excerpts(
 8895                buffer_1.clone(),
 8896                [
 8897                    ExcerptRange {
 8898                        context: 1..6,
 8899                        primary: None,
 8900                    },
 8901                    ExcerptRange {
 8902                        context: 12..15,
 8903                        primary: None,
 8904                    },
 8905                    ExcerptRange {
 8906                        context: 0..3,
 8907                        primary: None,
 8908                    },
 8909                ],
 8910                cx,
 8911            );
 8912            multibuffer.insert_excerpts_after(
 8913                excerpt_ids[0],
 8914                buffer_2.clone(),
 8915                [
 8916                    ExcerptRange {
 8917                        context: 8..12,
 8918                        primary: None,
 8919                    },
 8920                    ExcerptRange {
 8921                        context: 0..6,
 8922                        primary: None,
 8923                    },
 8924                ],
 8925                cx,
 8926            );
 8927        });
 8928    });
 8929
 8930    // Apply the update of adding the excerpts.
 8931    follower_1
 8932        .update(cx, |follower, cx| {
 8933            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8934        })
 8935        .await
 8936        .unwrap();
 8937    assert_eq!(
 8938        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8939        leader.update(cx, |editor, cx| editor.text(cx))
 8940    );
 8941    update_message.borrow_mut().take();
 8942
 8943    // Start following separately after it already has excerpts.
 8944    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8945    let follower_2 = cx
 8946        .update_window(*workspace.deref(), |_, cx| {
 8947            Editor::from_state_proto(
 8948                workspace.root_view(cx).unwrap().clone(),
 8949                ViewId {
 8950                    creator: Default::default(),
 8951                    id: 0,
 8952                },
 8953                &mut state_message,
 8954                cx,
 8955            )
 8956        })
 8957        .unwrap()
 8958        .unwrap()
 8959        .await
 8960        .unwrap();
 8961    assert_eq!(
 8962        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8963        leader.update(cx, |editor, cx| editor.text(cx))
 8964    );
 8965
 8966    // Remove some excerpts.
 8967    _ = leader.update(cx, |leader, cx| {
 8968        leader.buffer.update(cx, |multibuffer, cx| {
 8969            let excerpt_ids = multibuffer.excerpt_ids();
 8970            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8971            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8972        });
 8973    });
 8974
 8975    // Apply the update of removing the excerpts.
 8976    follower_1
 8977        .update(cx, |follower, cx| {
 8978            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8979        })
 8980        .await
 8981        .unwrap();
 8982    follower_2
 8983        .update(cx, |follower, cx| {
 8984            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8985        })
 8986        .await
 8987        .unwrap();
 8988    update_message.borrow_mut().take();
 8989    assert_eq!(
 8990        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8991        leader.update(cx, |editor, cx| editor.text(cx))
 8992    );
 8993}
 8994
 8995#[gpui::test]
 8996async fn go_to_prev_overlapping_diagnostic(
 8997    executor: BackgroundExecutor,
 8998    cx: &mut gpui::TestAppContext,
 8999) {
 9000    init_test(cx, |_| {});
 9001
 9002    let mut cx = EditorTestContext::new(cx).await;
 9003    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9004
 9005    cx.set_state(indoc! {"
 9006        ˇfn func(abc def: i32) -> u32 {
 9007        }
 9008    "});
 9009
 9010    _ = cx.update(|cx| {
 9011        _ = project.update(cx, |project, cx| {
 9012            project
 9013                .update_diagnostics(
 9014                    LanguageServerId(0),
 9015                    lsp::PublishDiagnosticsParams {
 9016                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9017                        version: None,
 9018                        diagnostics: vec![
 9019                            lsp::Diagnostic {
 9020                                range: lsp::Range::new(
 9021                                    lsp::Position::new(0, 11),
 9022                                    lsp::Position::new(0, 12),
 9023                                ),
 9024                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9025                                ..Default::default()
 9026                            },
 9027                            lsp::Diagnostic {
 9028                                range: lsp::Range::new(
 9029                                    lsp::Position::new(0, 12),
 9030                                    lsp::Position::new(0, 15),
 9031                                ),
 9032                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9033                                ..Default::default()
 9034                            },
 9035                            lsp::Diagnostic {
 9036                                range: lsp::Range::new(
 9037                                    lsp::Position::new(0, 25),
 9038                                    lsp::Position::new(0, 28),
 9039                                ),
 9040                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9041                                ..Default::default()
 9042                            },
 9043                        ],
 9044                    },
 9045                    &[],
 9046                    cx,
 9047                )
 9048                .unwrap()
 9049        });
 9050    });
 9051
 9052    executor.run_until_parked();
 9053
 9054    cx.update_editor(|editor, cx| {
 9055        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9056    });
 9057
 9058    cx.assert_editor_state(indoc! {"
 9059        fn func(abc def: i32) -> ˇu32 {
 9060        }
 9061    "});
 9062
 9063    cx.update_editor(|editor, cx| {
 9064        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9065    });
 9066
 9067    cx.assert_editor_state(indoc! {"
 9068        fn func(abc ˇdef: i32) -> u32 {
 9069        }
 9070    "});
 9071
 9072    cx.update_editor(|editor, cx| {
 9073        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9074    });
 9075
 9076    cx.assert_editor_state(indoc! {"
 9077        fn func(abcˇ def: i32) -> u32 {
 9078        }
 9079    "});
 9080
 9081    cx.update_editor(|editor, cx| {
 9082        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9083    });
 9084
 9085    cx.assert_editor_state(indoc! {"
 9086        fn func(abc def: i32) -> ˇu32 {
 9087        }
 9088    "});
 9089}
 9090
 9091#[gpui::test]
 9092async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9093    init_test(cx, |_| {});
 9094
 9095    let mut cx = EditorTestContext::new(cx).await;
 9096
 9097    let diff_base = r#"
 9098        use some::mod;
 9099
 9100        const A: u32 = 42;
 9101
 9102        fn main() {
 9103            println!("hello");
 9104
 9105            println!("world");
 9106        }
 9107        "#
 9108    .unindent();
 9109
 9110    // Edits are modified, removed, modified, added
 9111    cx.set_state(
 9112        &r#"
 9113        use some::modified;
 9114
 9115        ˇ
 9116        fn main() {
 9117            println!("hello there");
 9118
 9119            println!("around the");
 9120            println!("world");
 9121        }
 9122        "#
 9123        .unindent(),
 9124    );
 9125
 9126    cx.set_diff_base(Some(&diff_base));
 9127    executor.run_until_parked();
 9128
 9129    cx.update_editor(|editor, cx| {
 9130        //Wrap around the bottom of the buffer
 9131        for _ in 0..3 {
 9132            editor.go_to_hunk(&GoToHunk, cx);
 9133        }
 9134    });
 9135
 9136    cx.assert_editor_state(
 9137        &r#"
 9138        ˇuse some::modified;
 9139
 9140
 9141        fn main() {
 9142            println!("hello there");
 9143
 9144            println!("around the");
 9145            println!("world");
 9146        }
 9147        "#
 9148        .unindent(),
 9149    );
 9150
 9151    cx.update_editor(|editor, cx| {
 9152        //Wrap around the top of the buffer
 9153        for _ in 0..2 {
 9154            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9155        }
 9156    });
 9157
 9158    cx.assert_editor_state(
 9159        &r#"
 9160        use some::modified;
 9161
 9162
 9163        fn main() {
 9164        ˇ    println!("hello there");
 9165
 9166            println!("around the");
 9167            println!("world");
 9168        }
 9169        "#
 9170        .unindent(),
 9171    );
 9172
 9173    cx.update_editor(|editor, cx| {
 9174        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9175    });
 9176
 9177    cx.assert_editor_state(
 9178        &r#"
 9179        use some::modified;
 9180
 9181        ˇ
 9182        fn main() {
 9183            println!("hello there");
 9184
 9185            println!("around the");
 9186            println!("world");
 9187        }
 9188        "#
 9189        .unindent(),
 9190    );
 9191
 9192    cx.update_editor(|editor, cx| {
 9193        for _ in 0..3 {
 9194            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9195        }
 9196    });
 9197
 9198    cx.assert_editor_state(
 9199        &r#"
 9200        use some::modified;
 9201
 9202
 9203        fn main() {
 9204        ˇ    println!("hello there");
 9205
 9206            println!("around the");
 9207            println!("world");
 9208        }
 9209        "#
 9210        .unindent(),
 9211    );
 9212
 9213    cx.update_editor(|editor, cx| {
 9214        editor.fold(&Fold, cx);
 9215
 9216        //Make sure that the fold only gets one hunk
 9217        for _ in 0..4 {
 9218            editor.go_to_hunk(&GoToHunk, cx);
 9219        }
 9220    });
 9221
 9222    cx.assert_editor_state(
 9223        &r#"
 9224        ˇuse some::modified;
 9225
 9226
 9227        fn main() {
 9228            println!("hello there");
 9229
 9230            println!("around the");
 9231            println!("world");
 9232        }
 9233        "#
 9234        .unindent(),
 9235    );
 9236}
 9237
 9238#[test]
 9239fn test_split_words() {
 9240    fn split(text: &str) -> Vec<&str> {
 9241        split_words(text).collect()
 9242    }
 9243
 9244    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9245    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9246    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9247    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9248    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9249    assert_eq!(split("helloworld"), &["helloworld"]);
 9250
 9251    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9252}
 9253
 9254#[gpui::test]
 9255async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9256    init_test(cx, |_| {});
 9257
 9258    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9259    let mut assert = |before, after| {
 9260        let _state_context = cx.set_state(before);
 9261        cx.update_editor(|editor, cx| {
 9262            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9263        });
 9264        cx.assert_editor_state(after);
 9265    };
 9266
 9267    // Outside bracket jumps to outside of matching bracket
 9268    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9269    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9270
 9271    // Inside bracket jumps to inside of matching bracket
 9272    assert("console.log(ˇvar);", "console.log(varˇ);");
 9273    assert("console.log(varˇ);", "console.log(ˇvar);");
 9274
 9275    // When outside a bracket and inside, favor jumping to the inside bracket
 9276    assert(
 9277        "console.log('foo', [1, 2, 3]ˇ);",
 9278        "console.log(ˇ'foo', [1, 2, 3]);",
 9279    );
 9280    assert(
 9281        "console.log(ˇ'foo', [1, 2, 3]);",
 9282        "console.log('foo', [1, 2, 3]ˇ);",
 9283    );
 9284
 9285    // Bias forward if two options are equally likely
 9286    assert(
 9287        "let result = curried_fun()ˇ();",
 9288        "let result = curried_fun()()ˇ;",
 9289    );
 9290
 9291    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9292    assert(
 9293        indoc! {"
 9294            function test() {
 9295                console.log('test')ˇ
 9296            }"},
 9297        indoc! {"
 9298            function test() {
 9299                console.logˇ('test')
 9300            }"},
 9301    );
 9302}
 9303
 9304#[gpui::test]
 9305async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9306    init_test(cx, |_| {});
 9307
 9308    let fs = FakeFs::new(cx.executor());
 9309    fs.insert_tree(
 9310        "/a",
 9311        json!({
 9312            "main.rs": "fn main() { let a = 5; }",
 9313            "other.rs": "// Test file",
 9314        }),
 9315    )
 9316    .await;
 9317    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9318
 9319    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9320    language_registry.add(Arc::new(Language::new(
 9321        LanguageConfig {
 9322            name: "Rust".into(),
 9323            matcher: LanguageMatcher {
 9324                path_suffixes: vec!["rs".to_string()],
 9325                ..Default::default()
 9326            },
 9327            brackets: BracketPairConfig {
 9328                pairs: vec![BracketPair {
 9329                    start: "{".to_string(),
 9330                    end: "}".to_string(),
 9331                    close: true,
 9332                    surround: true,
 9333                    newline: true,
 9334                }],
 9335                disabled_scopes_by_bracket_ix: Vec::new(),
 9336            },
 9337            ..Default::default()
 9338        },
 9339        Some(tree_sitter_rust::language()),
 9340    )));
 9341    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9342        "Rust",
 9343        FakeLspAdapter {
 9344            capabilities: lsp::ServerCapabilities {
 9345                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9346                    first_trigger_character: "{".to_string(),
 9347                    more_trigger_character: None,
 9348                }),
 9349                ..Default::default()
 9350            },
 9351            ..Default::default()
 9352        },
 9353    );
 9354
 9355    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9356
 9357    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9358
 9359    let worktree_id = workspace
 9360        .update(cx, |workspace, cx| {
 9361            workspace.project().update(cx, |project, cx| {
 9362                project.worktrees(cx).next().unwrap().read(cx).id()
 9363            })
 9364        })
 9365        .unwrap();
 9366
 9367    let buffer = project
 9368        .update(cx, |project, cx| {
 9369            project.open_local_buffer("/a/main.rs", cx)
 9370        })
 9371        .await
 9372        .unwrap();
 9373    cx.executor().run_until_parked();
 9374    cx.executor().start_waiting();
 9375    let fake_server = fake_servers.next().await.unwrap();
 9376    let editor_handle = workspace
 9377        .update(cx, |workspace, cx| {
 9378            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9379        })
 9380        .unwrap()
 9381        .await
 9382        .unwrap()
 9383        .downcast::<Editor>()
 9384        .unwrap();
 9385
 9386    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9387        assert_eq!(
 9388            params.text_document_position.text_document.uri,
 9389            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9390        );
 9391        assert_eq!(
 9392            params.text_document_position.position,
 9393            lsp::Position::new(0, 21),
 9394        );
 9395
 9396        Ok(Some(vec![lsp::TextEdit {
 9397            new_text: "]".to_string(),
 9398            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9399        }]))
 9400    });
 9401
 9402    editor_handle.update(cx, |editor, cx| {
 9403        editor.focus(cx);
 9404        editor.change_selections(None, cx, |s| {
 9405            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9406        });
 9407        editor.handle_input("{", cx);
 9408    });
 9409
 9410    cx.executor().run_until_parked();
 9411
 9412    _ = buffer.update(cx, |buffer, _| {
 9413        assert_eq!(
 9414            buffer.text(),
 9415            "fn main() { let a = {5}; }",
 9416            "No extra braces from on type formatting should appear in the buffer"
 9417        )
 9418    });
 9419}
 9420
 9421#[gpui::test]
 9422async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9423    init_test(cx, |_| {});
 9424
 9425    let fs = FakeFs::new(cx.executor());
 9426    fs.insert_tree(
 9427        "/a",
 9428        json!({
 9429            "main.rs": "fn main() { let a = 5; }",
 9430            "other.rs": "// Test file",
 9431        }),
 9432    )
 9433    .await;
 9434
 9435    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9436
 9437    let server_restarts = Arc::new(AtomicUsize::new(0));
 9438    let closure_restarts = Arc::clone(&server_restarts);
 9439    let language_server_name = "test language server";
 9440    let language_name: Arc<str> = "Rust".into();
 9441
 9442    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9443    language_registry.add(Arc::new(Language::new(
 9444        LanguageConfig {
 9445            name: Arc::clone(&language_name),
 9446            matcher: LanguageMatcher {
 9447                path_suffixes: vec!["rs".to_string()],
 9448                ..Default::default()
 9449            },
 9450            ..Default::default()
 9451        },
 9452        Some(tree_sitter_rust::language()),
 9453    )));
 9454    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9455        "Rust",
 9456        FakeLspAdapter {
 9457            name: language_server_name,
 9458            initialization_options: Some(json!({
 9459                "testOptionValue": true
 9460            })),
 9461            initializer: Some(Box::new(move |fake_server| {
 9462                let task_restarts = Arc::clone(&closure_restarts);
 9463                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9464                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9465                    futures::future::ready(Ok(()))
 9466                });
 9467            })),
 9468            ..Default::default()
 9469        },
 9470    );
 9471
 9472    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9473    let _buffer = project
 9474        .update(cx, |project, cx| {
 9475            project.open_local_buffer("/a/main.rs", cx)
 9476        })
 9477        .await
 9478        .unwrap();
 9479    let _fake_server = fake_servers.next().await.unwrap();
 9480    update_test_language_settings(cx, |language_settings| {
 9481        language_settings.languages.insert(
 9482            Arc::clone(&language_name),
 9483            LanguageSettingsContent {
 9484                tab_size: NonZeroU32::new(8),
 9485                ..Default::default()
 9486            },
 9487        );
 9488    });
 9489    cx.executor().run_until_parked();
 9490    assert_eq!(
 9491        server_restarts.load(atomic::Ordering::Acquire),
 9492        0,
 9493        "Should not restart LSP server on an unrelated change"
 9494    );
 9495
 9496    update_test_project_settings(cx, |project_settings| {
 9497        project_settings.lsp.insert(
 9498            "Some other server name".into(),
 9499            LspSettings {
 9500                binary: None,
 9501                settings: None,
 9502                initialization_options: Some(json!({
 9503                    "some other init value": false
 9504                })),
 9505            },
 9506        );
 9507    });
 9508    cx.executor().run_until_parked();
 9509    assert_eq!(
 9510        server_restarts.load(atomic::Ordering::Acquire),
 9511        0,
 9512        "Should not restart LSP server on an unrelated LSP settings change"
 9513    );
 9514
 9515    update_test_project_settings(cx, |project_settings| {
 9516        project_settings.lsp.insert(
 9517            language_server_name.into(),
 9518            LspSettings {
 9519                binary: None,
 9520                settings: None,
 9521                initialization_options: Some(json!({
 9522                    "anotherInitValue": false
 9523                })),
 9524            },
 9525        );
 9526    });
 9527    cx.executor().run_until_parked();
 9528    assert_eq!(
 9529        server_restarts.load(atomic::Ordering::Acquire),
 9530        1,
 9531        "Should restart LSP server on a related LSP settings change"
 9532    );
 9533
 9534    update_test_project_settings(cx, |project_settings| {
 9535        project_settings.lsp.insert(
 9536            language_server_name.into(),
 9537            LspSettings {
 9538                binary: None,
 9539                settings: None,
 9540                initialization_options: Some(json!({
 9541                    "anotherInitValue": false
 9542                })),
 9543            },
 9544        );
 9545    });
 9546    cx.executor().run_until_parked();
 9547    assert_eq!(
 9548        server_restarts.load(atomic::Ordering::Acquire),
 9549        1,
 9550        "Should not restart LSP server on a related LSP settings change that is the same"
 9551    );
 9552
 9553    update_test_project_settings(cx, |project_settings| {
 9554        project_settings.lsp.insert(
 9555            language_server_name.into(),
 9556            LspSettings {
 9557                binary: None,
 9558                settings: None,
 9559                initialization_options: None,
 9560            },
 9561        );
 9562    });
 9563    cx.executor().run_until_parked();
 9564    assert_eq!(
 9565        server_restarts.load(atomic::Ordering::Acquire),
 9566        2,
 9567        "Should restart LSP server on another related LSP settings change"
 9568    );
 9569}
 9570
 9571#[gpui::test]
 9572async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9573    init_test(cx, |_| {});
 9574
 9575    let mut cx = EditorLspTestContext::new_rust(
 9576        lsp::ServerCapabilities {
 9577            completion_provider: Some(lsp::CompletionOptions {
 9578                trigger_characters: Some(vec![".".to_string()]),
 9579                resolve_provider: Some(true),
 9580                ..Default::default()
 9581            }),
 9582            ..Default::default()
 9583        },
 9584        cx,
 9585    )
 9586    .await;
 9587
 9588    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9589    cx.simulate_keystroke(".");
 9590    let completion_item = lsp::CompletionItem {
 9591        label: "some".into(),
 9592        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9593        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9594        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9595            kind: lsp::MarkupKind::Markdown,
 9596            value: "```rust\nSome(2)\n```".to_string(),
 9597        })),
 9598        deprecated: Some(false),
 9599        sort_text: Some("fffffff2".to_string()),
 9600        filter_text: Some("some".to_string()),
 9601        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9602        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9603            range: lsp::Range {
 9604                start: lsp::Position {
 9605                    line: 0,
 9606                    character: 22,
 9607                },
 9608                end: lsp::Position {
 9609                    line: 0,
 9610                    character: 22,
 9611                },
 9612            },
 9613            new_text: "Some(2)".to_string(),
 9614        })),
 9615        additional_text_edits: Some(vec![lsp::TextEdit {
 9616            range: lsp::Range {
 9617                start: lsp::Position {
 9618                    line: 0,
 9619                    character: 20,
 9620                },
 9621                end: lsp::Position {
 9622                    line: 0,
 9623                    character: 22,
 9624                },
 9625            },
 9626            new_text: "".to_string(),
 9627        }]),
 9628        ..Default::default()
 9629    };
 9630
 9631    let closure_completion_item = completion_item.clone();
 9632    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9633        let task_completion_item = closure_completion_item.clone();
 9634        async move {
 9635            Ok(Some(lsp::CompletionResponse::Array(vec![
 9636                task_completion_item,
 9637            ])))
 9638        }
 9639    });
 9640
 9641    request.next().await;
 9642
 9643    cx.condition(|editor, _| editor.context_menu_visible())
 9644        .await;
 9645    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9646        editor
 9647            .confirm_completion(&ConfirmCompletion::default(), cx)
 9648            .unwrap()
 9649    });
 9650    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9651
 9652    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9653        let task_completion_item = completion_item.clone();
 9654        async move { Ok(task_completion_item) }
 9655    })
 9656    .next()
 9657    .await
 9658    .unwrap();
 9659    apply_additional_edits.await.unwrap();
 9660    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9661}
 9662
 9663#[gpui::test]
 9664async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9665    init_test(cx, |_| {});
 9666
 9667    let mut cx = EditorLspTestContext::new(
 9668        Language::new(
 9669            LanguageConfig {
 9670                matcher: LanguageMatcher {
 9671                    path_suffixes: vec!["jsx".into()],
 9672                    ..Default::default()
 9673                },
 9674                overrides: [(
 9675                    "element".into(),
 9676                    LanguageConfigOverride {
 9677                        word_characters: Override::Set(['-'].into_iter().collect()),
 9678                        ..Default::default()
 9679                    },
 9680                )]
 9681                .into_iter()
 9682                .collect(),
 9683                ..Default::default()
 9684            },
 9685            Some(tree_sitter_typescript::language_tsx()),
 9686        )
 9687        .with_override_query("(jsx_self_closing_element) @element")
 9688        .unwrap(),
 9689        lsp::ServerCapabilities {
 9690            completion_provider: Some(lsp::CompletionOptions {
 9691                trigger_characters: Some(vec![":".to_string()]),
 9692                ..Default::default()
 9693            }),
 9694            ..Default::default()
 9695        },
 9696        cx,
 9697    )
 9698    .await;
 9699
 9700    cx.lsp
 9701        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9702            Ok(Some(lsp::CompletionResponse::Array(vec![
 9703                lsp::CompletionItem {
 9704                    label: "bg-blue".into(),
 9705                    ..Default::default()
 9706                },
 9707                lsp::CompletionItem {
 9708                    label: "bg-red".into(),
 9709                    ..Default::default()
 9710                },
 9711                lsp::CompletionItem {
 9712                    label: "bg-yellow".into(),
 9713                    ..Default::default()
 9714                },
 9715            ])))
 9716        });
 9717
 9718    cx.set_state(r#"<p class="bgˇ" />"#);
 9719
 9720    // Trigger completion when typing a dash, because the dash is an extra
 9721    // word character in the 'element' scope, which contains the cursor.
 9722    cx.simulate_keystroke("-");
 9723    cx.executor().run_until_parked();
 9724    cx.update_editor(|editor, _| {
 9725        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9726            assert_eq!(
 9727                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9728                &["bg-red", "bg-blue", "bg-yellow"]
 9729            );
 9730        } else {
 9731            panic!("expected completion menu to be open");
 9732        }
 9733    });
 9734
 9735    cx.simulate_keystroke("l");
 9736    cx.executor().run_until_parked();
 9737    cx.update_editor(|editor, _| {
 9738        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9739            assert_eq!(
 9740                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9741                &["bg-blue", "bg-yellow"]
 9742            );
 9743        } else {
 9744            panic!("expected completion menu to be open");
 9745        }
 9746    });
 9747
 9748    // When filtering completions, consider the character after the '-' to
 9749    // be the start of a subword.
 9750    cx.set_state(r#"<p class="yelˇ" />"#);
 9751    cx.simulate_keystroke("l");
 9752    cx.executor().run_until_parked();
 9753    cx.update_editor(|editor, _| {
 9754        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9755            assert_eq!(
 9756                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9757                &["bg-yellow"]
 9758            );
 9759        } else {
 9760            panic!("expected completion menu to be open");
 9761        }
 9762    });
 9763}
 9764
 9765#[gpui::test]
 9766async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9767    init_test(cx, |settings| {
 9768        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9769            FormatterList(vec![Formatter::Prettier].into()),
 9770        ))
 9771    });
 9772
 9773    let fs = FakeFs::new(cx.executor());
 9774    fs.insert_file("/file.ts", Default::default()).await;
 9775
 9776    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9777    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9778
 9779    language_registry.add(Arc::new(Language::new(
 9780        LanguageConfig {
 9781            name: "TypeScript".into(),
 9782            matcher: LanguageMatcher {
 9783                path_suffixes: vec!["ts".to_string()],
 9784                ..Default::default()
 9785            },
 9786            ..Default::default()
 9787        },
 9788        Some(tree_sitter_rust::language()),
 9789    )));
 9790    update_test_language_settings(cx, |settings| {
 9791        settings.defaults.prettier = Some(PrettierSettings {
 9792            allowed: true,
 9793            ..PrettierSettings::default()
 9794        });
 9795    });
 9796
 9797    let test_plugin = "test_plugin";
 9798    let _ = language_registry.register_fake_lsp_adapter(
 9799        "TypeScript",
 9800        FakeLspAdapter {
 9801            prettier_plugins: vec![test_plugin],
 9802            ..Default::default()
 9803        },
 9804    );
 9805
 9806    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9807    let buffer = project
 9808        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9809        .await
 9810        .unwrap();
 9811
 9812    let buffer_text = "one\ntwo\nthree\n";
 9813    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9814    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9815    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9816
 9817    editor
 9818        .update(cx, |editor, cx| {
 9819            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9820        })
 9821        .unwrap()
 9822        .await;
 9823    assert_eq!(
 9824        editor.update(cx, |editor, cx| editor.text(cx)),
 9825        buffer_text.to_string() + prettier_format_suffix,
 9826        "Test prettier formatting was not applied to the original buffer text",
 9827    );
 9828
 9829    update_test_language_settings(cx, |settings| {
 9830        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9831    });
 9832    let format = editor.update(cx, |editor, cx| {
 9833        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9834    });
 9835    format.await.unwrap();
 9836    assert_eq!(
 9837        editor.update(cx, |editor, cx| editor.text(cx)),
 9838        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9839        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9840    );
 9841}
 9842
 9843#[gpui::test]
 9844async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9845    init_test(cx, |_| {});
 9846    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9847    let base_text = indoc! {r#"struct Row;
 9848struct Row1;
 9849struct Row2;
 9850
 9851struct Row4;
 9852struct Row5;
 9853struct Row6;
 9854
 9855struct Row8;
 9856struct Row9;
 9857struct Row10;"#};
 9858
 9859    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9860    assert_hunk_revert(
 9861        indoc! {r#"struct Row;
 9862                   struct Row1;
 9863                   struct Row1.1;
 9864                   struct Row1.2;
 9865                   struct Row2;ˇ
 9866
 9867                   struct Row4;
 9868                   struct Row5;
 9869                   struct Row6;
 9870
 9871                   struct Row8;
 9872                   ˇstruct Row9;
 9873                   struct Row9.1;
 9874                   struct Row9.2;
 9875                   struct Row9.3;
 9876                   struct Row10;"#},
 9877        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9878        indoc! {r#"struct Row;
 9879                   struct Row1;
 9880                   struct Row1.1;
 9881                   struct Row1.2;
 9882                   struct Row2;ˇ
 9883
 9884                   struct Row4;
 9885                   struct Row5;
 9886                   struct Row6;
 9887
 9888                   struct Row8;
 9889                   ˇstruct Row9;
 9890                   struct Row9.1;
 9891                   struct Row9.2;
 9892                   struct Row9.3;
 9893                   struct Row10;"#},
 9894        base_text,
 9895        &mut cx,
 9896    );
 9897    // Same for selections
 9898    assert_hunk_revert(
 9899        indoc! {r#"struct Row;
 9900                   struct Row1;
 9901                   struct Row2;
 9902                   struct Row2.1;
 9903                   struct Row2.2;
 9904                   «ˇ
 9905                   struct Row4;
 9906                   struct» Row5;
 9907                   «struct Row6;
 9908                   ˇ»
 9909                   struct Row9.1;
 9910                   struct Row9.2;
 9911                   struct Row9.3;
 9912                   struct Row8;
 9913                   struct Row9;
 9914                   struct Row10;"#},
 9915        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9916        indoc! {r#"struct Row;
 9917                   struct Row1;
 9918                   struct Row2;
 9919                   struct Row2.1;
 9920                   struct Row2.2;
 9921                   «ˇ
 9922                   struct Row4;
 9923                   struct» Row5;
 9924                   «struct Row6;
 9925                   ˇ»
 9926                   struct Row9.1;
 9927                   struct Row9.2;
 9928                   struct Row9.3;
 9929                   struct Row8;
 9930                   struct Row9;
 9931                   struct Row10;"#},
 9932        base_text,
 9933        &mut cx,
 9934    );
 9935
 9936    // When carets and selections intersect the addition hunks, those are reverted.
 9937    // Adjacent carets got merged.
 9938    assert_hunk_revert(
 9939        indoc! {r#"struct Row;
 9940                   ˇ// something on the top
 9941                   struct Row1;
 9942                   struct Row2;
 9943                   struct Roˇw3.1;
 9944                   struct Row2.2;
 9945                   struct Row2.3;ˇ
 9946
 9947                   struct Row4;
 9948                   struct ˇRow5.1;
 9949                   struct Row5.2;
 9950                   struct «Rowˇ»5.3;
 9951                   struct Row5;
 9952                   struct Row6;
 9953                   ˇ
 9954                   struct Row9.1;
 9955                   struct «Rowˇ»9.2;
 9956                   struct «ˇRow»9.3;
 9957                   struct Row8;
 9958                   struct Row9;
 9959                   «ˇ// something on bottom»
 9960                   struct Row10;"#},
 9961        vec![
 9962            DiffHunkStatus::Added,
 9963            DiffHunkStatus::Added,
 9964            DiffHunkStatus::Added,
 9965            DiffHunkStatus::Added,
 9966            DiffHunkStatus::Added,
 9967        ],
 9968        indoc! {r#"struct Row;
 9969                   ˇstruct Row1;
 9970                   struct Row2;
 9971                   ˇ
 9972                   struct Row4;
 9973                   ˇstruct Row5;
 9974                   struct Row6;
 9975                   ˇ
 9976                   ˇstruct Row8;
 9977                   struct Row9;
 9978                   ˇstruct Row10;"#},
 9979        base_text,
 9980        &mut cx,
 9981    );
 9982}
 9983
 9984#[gpui::test]
 9985async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9986    init_test(cx, |_| {});
 9987    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9988    let base_text = indoc! {r#"struct Row;
 9989struct Row1;
 9990struct Row2;
 9991
 9992struct Row4;
 9993struct Row5;
 9994struct Row6;
 9995
 9996struct Row8;
 9997struct Row9;
 9998struct Row10;"#};
 9999
10000    // Modification hunks behave the same as the addition ones.
10001    assert_hunk_revert(
10002        indoc! {r#"struct Row;
10003                   struct Row1;
10004                   struct Row33;
10005                   ˇ
10006                   struct Row4;
10007                   struct Row5;
10008                   struct Row6;
10009                   ˇ
10010                   struct Row99;
10011                   struct Row9;
10012                   struct Row10;"#},
10013        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10014        indoc! {r#"struct Row;
10015                   struct Row1;
10016                   struct Row33;
10017                   ˇ
10018                   struct Row4;
10019                   struct Row5;
10020                   struct Row6;
10021                   ˇ
10022                   struct Row99;
10023                   struct Row9;
10024                   struct Row10;"#},
10025        base_text,
10026        &mut cx,
10027    );
10028    assert_hunk_revert(
10029        indoc! {r#"struct Row;
10030                   struct Row1;
10031                   struct Row33;
10032                   «ˇ
10033                   struct Row4;
10034                   struct» Row5;
10035                   «struct Row6;
10036                   ˇ»
10037                   struct Row99;
10038                   struct Row9;
10039                   struct Row10;"#},
10040        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10041        indoc! {r#"struct Row;
10042                   struct Row1;
10043                   struct Row33;
10044                   «ˇ
10045                   struct Row4;
10046                   struct» Row5;
10047                   «struct Row6;
10048                   ˇ»
10049                   struct Row99;
10050                   struct Row9;
10051                   struct Row10;"#},
10052        base_text,
10053        &mut cx,
10054    );
10055
10056    assert_hunk_revert(
10057        indoc! {r#"ˇstruct Row1.1;
10058                   struct Row1;
10059                   «ˇstr»uct Row22;
10060
10061                   struct ˇRow44;
10062                   struct Row5;
10063                   struct «Rˇ»ow66;ˇ
10064
10065                   «struˇ»ct Row88;
10066                   struct Row9;
10067                   struct Row1011;ˇ"#},
10068        vec![
10069            DiffHunkStatus::Modified,
10070            DiffHunkStatus::Modified,
10071            DiffHunkStatus::Modified,
10072            DiffHunkStatus::Modified,
10073            DiffHunkStatus::Modified,
10074            DiffHunkStatus::Modified,
10075        ],
10076        indoc! {r#"struct Row;
10077                   ˇstruct Row1;
10078                   struct Row2;
10079                   ˇ
10080                   struct Row4;
10081                   ˇstruct Row5;
10082                   struct Row6;
10083                   ˇ
10084                   struct Row8;
10085                   ˇstruct Row9;
10086                   struct Row10;ˇ"#},
10087        base_text,
10088        &mut cx,
10089    );
10090}
10091
10092#[gpui::test]
10093async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10094    init_test(cx, |_| {});
10095    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10096    let base_text = indoc! {r#"struct Row;
10097struct Row1;
10098struct Row2;
10099
10100struct Row4;
10101struct Row5;
10102struct Row6;
10103
10104struct Row8;
10105struct Row9;
10106struct Row10;"#};
10107
10108    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10109    assert_hunk_revert(
10110        indoc! {r#"struct Row;
10111                   struct Row2;
10112
10113                   ˇstruct Row4;
10114                   struct Row5;
10115                   struct Row6;
10116                   ˇ
10117                   struct Row8;
10118                   struct Row10;"#},
10119        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10120        indoc! {r#"struct Row;
10121                   struct Row2;
10122
10123                   ˇstruct Row4;
10124                   struct Row5;
10125                   struct Row6;
10126                   ˇ
10127                   struct Row8;
10128                   struct Row10;"#},
10129        base_text,
10130        &mut cx,
10131    );
10132    assert_hunk_revert(
10133        indoc! {r#"struct Row;
10134                   struct Row2;
10135
10136                   «ˇstruct Row4;
10137                   struct» Row5;
10138                   «struct Row6;
10139                   ˇ»
10140                   struct Row8;
10141                   struct Row10;"#},
10142        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10143        indoc! {r#"struct Row;
10144                   struct Row2;
10145
10146                   «ˇstruct Row4;
10147                   struct» Row5;
10148                   «struct Row6;
10149                   ˇ»
10150                   struct Row8;
10151                   struct Row10;"#},
10152        base_text,
10153        &mut cx,
10154    );
10155
10156    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10157    assert_hunk_revert(
10158        indoc! {r#"struct Row;
10159                   ˇstruct Row2;
10160
10161                   struct Row4;
10162                   struct Row5;
10163                   struct Row6;
10164
10165                   struct Row8;ˇ
10166                   struct Row10;"#},
10167        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10168        indoc! {r#"struct Row;
10169                   struct Row1;
10170                   ˇstruct Row2;
10171
10172                   struct Row4;
10173                   struct Row5;
10174                   struct Row6;
10175
10176                   struct Row8;ˇ
10177                   struct Row9;
10178                   struct Row10;"#},
10179        base_text,
10180        &mut cx,
10181    );
10182    assert_hunk_revert(
10183        indoc! {r#"struct Row;
10184                   struct Row2«ˇ;
10185                   struct Row4;
10186                   struct» Row5;
10187                   «struct Row6;
10188
10189                   struct Row8;ˇ»
10190                   struct Row10;"#},
10191        vec![
10192            DiffHunkStatus::Removed,
10193            DiffHunkStatus::Removed,
10194            DiffHunkStatus::Removed,
10195        ],
10196        indoc! {r#"struct Row;
10197                   struct Row1;
10198                   struct Row2«ˇ;
10199
10200                   struct Row4;
10201                   struct» Row5;
10202                   «struct Row6;
10203
10204                   struct Row8;ˇ»
10205                   struct Row9;
10206                   struct Row10;"#},
10207        base_text,
10208        &mut cx,
10209    );
10210}
10211
10212#[gpui::test]
10213async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10214    init_test(cx, |_| {});
10215
10216    let cols = 4;
10217    let rows = 10;
10218    let sample_text_1 = sample_text(rows, cols, 'a');
10219    assert_eq!(
10220        sample_text_1,
10221        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10222    );
10223    let sample_text_2 = sample_text(rows, cols, 'l');
10224    assert_eq!(
10225        sample_text_2,
10226        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10227    );
10228    let sample_text_3 = sample_text(rows, cols, 'v');
10229    assert_eq!(
10230        sample_text_3,
10231        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10232    );
10233
10234    fn diff_every_buffer_row(
10235        buffer: &Model<Buffer>,
10236        sample_text: String,
10237        cols: usize,
10238        cx: &mut gpui::TestAppContext,
10239    ) {
10240        // revert first character in each row, creating one large diff hunk per buffer
10241        let is_first_char = |offset: usize| offset % cols == 0;
10242        buffer.update(cx, |buffer, cx| {
10243            buffer.set_text(
10244                sample_text
10245                    .chars()
10246                    .enumerate()
10247                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10248                    .collect::<String>(),
10249                cx,
10250            );
10251            buffer.set_diff_base(Some(sample_text), cx);
10252        });
10253        cx.executor().run_until_parked();
10254    }
10255
10256    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10257    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10258
10259    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10260    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10261
10262    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10263    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10264
10265    let multibuffer = cx.new_model(|cx| {
10266        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10267        multibuffer.push_excerpts(
10268            buffer_1.clone(),
10269            [
10270                ExcerptRange {
10271                    context: Point::new(0, 0)..Point::new(3, 0),
10272                    primary: None,
10273                },
10274                ExcerptRange {
10275                    context: Point::new(5, 0)..Point::new(7, 0),
10276                    primary: None,
10277                },
10278                ExcerptRange {
10279                    context: Point::new(9, 0)..Point::new(10, 4),
10280                    primary: None,
10281                },
10282            ],
10283            cx,
10284        );
10285        multibuffer.push_excerpts(
10286            buffer_2.clone(),
10287            [
10288                ExcerptRange {
10289                    context: Point::new(0, 0)..Point::new(3, 0),
10290                    primary: None,
10291                },
10292                ExcerptRange {
10293                    context: Point::new(5, 0)..Point::new(7, 0),
10294                    primary: None,
10295                },
10296                ExcerptRange {
10297                    context: Point::new(9, 0)..Point::new(10, 4),
10298                    primary: None,
10299                },
10300            ],
10301            cx,
10302        );
10303        multibuffer.push_excerpts(
10304            buffer_3.clone(),
10305            [
10306                ExcerptRange {
10307                    context: Point::new(0, 0)..Point::new(3, 0),
10308                    primary: None,
10309                },
10310                ExcerptRange {
10311                    context: Point::new(5, 0)..Point::new(7, 0),
10312                    primary: None,
10313                },
10314                ExcerptRange {
10315                    context: Point::new(9, 0)..Point::new(10, 4),
10316                    primary: None,
10317                },
10318            ],
10319            cx,
10320        );
10321        multibuffer
10322    });
10323
10324    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10325    editor.update(cx, |editor, cx| {
10326        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");
10327        editor.select_all(&SelectAll, cx);
10328        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10329    });
10330    cx.executor().run_until_parked();
10331    // When all ranges are selected, all buffer hunks are reverted.
10332    editor.update(cx, |editor, cx| {
10333        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");
10334    });
10335    buffer_1.update(cx, |buffer, _| {
10336        assert_eq!(buffer.text(), sample_text_1);
10337    });
10338    buffer_2.update(cx, |buffer, _| {
10339        assert_eq!(buffer.text(), sample_text_2);
10340    });
10341    buffer_3.update(cx, |buffer, _| {
10342        assert_eq!(buffer.text(), sample_text_3);
10343    });
10344
10345    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10346    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10347    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10348    editor.update(cx, |editor, cx| {
10349        editor.change_selections(None, cx, |s| {
10350            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10351        });
10352        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10353    });
10354    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10355    // but not affect buffer_2 and its related excerpts.
10356    editor.update(cx, |editor, cx| {
10357        assert_eq!(
10358            editor.text(cx),
10359            "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"
10360        );
10361    });
10362    buffer_1.update(cx, |buffer, _| {
10363        assert_eq!(buffer.text(), sample_text_1);
10364    });
10365    buffer_2.update(cx, |buffer, _| {
10366        assert_eq!(
10367            buffer.text(),
10368            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10369        );
10370    });
10371    buffer_3.update(cx, |buffer, _| {
10372        assert_eq!(
10373            buffer.text(),
10374            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10375        );
10376    });
10377}
10378
10379#[gpui::test]
10380async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10381    init_test(cx, |_| {});
10382
10383    let cols = 4;
10384    let rows = 10;
10385    let sample_text_1 = sample_text(rows, cols, 'a');
10386    assert_eq!(
10387        sample_text_1,
10388        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10389    );
10390    let sample_text_2 = sample_text(rows, cols, 'l');
10391    assert_eq!(
10392        sample_text_2,
10393        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10394    );
10395    let sample_text_3 = sample_text(rows, cols, 'v');
10396    assert_eq!(
10397        sample_text_3,
10398        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10399    );
10400
10401    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10402    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10403    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10404
10405    let multi_buffer = cx.new_model(|cx| {
10406        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10407        multibuffer.push_excerpts(
10408            buffer_1.clone(),
10409            [
10410                ExcerptRange {
10411                    context: Point::new(0, 0)..Point::new(3, 0),
10412                    primary: None,
10413                },
10414                ExcerptRange {
10415                    context: Point::new(5, 0)..Point::new(7, 0),
10416                    primary: None,
10417                },
10418                ExcerptRange {
10419                    context: Point::new(9, 0)..Point::new(10, 4),
10420                    primary: None,
10421                },
10422            ],
10423            cx,
10424        );
10425        multibuffer.push_excerpts(
10426            buffer_2.clone(),
10427            [
10428                ExcerptRange {
10429                    context: Point::new(0, 0)..Point::new(3, 0),
10430                    primary: None,
10431                },
10432                ExcerptRange {
10433                    context: Point::new(5, 0)..Point::new(7, 0),
10434                    primary: None,
10435                },
10436                ExcerptRange {
10437                    context: Point::new(9, 0)..Point::new(10, 4),
10438                    primary: None,
10439                },
10440            ],
10441            cx,
10442        );
10443        multibuffer.push_excerpts(
10444            buffer_3.clone(),
10445            [
10446                ExcerptRange {
10447                    context: Point::new(0, 0)..Point::new(3, 0),
10448                    primary: None,
10449                },
10450                ExcerptRange {
10451                    context: Point::new(5, 0)..Point::new(7, 0),
10452                    primary: None,
10453                },
10454                ExcerptRange {
10455                    context: Point::new(9, 0)..Point::new(10, 4),
10456                    primary: None,
10457                },
10458            ],
10459            cx,
10460        );
10461        multibuffer
10462    });
10463
10464    let fs = FakeFs::new(cx.executor());
10465    fs.insert_tree(
10466        "/a",
10467        json!({
10468            "main.rs": sample_text_1,
10469            "other.rs": sample_text_2,
10470            "lib.rs": sample_text_3,
10471        }),
10472    )
10473    .await;
10474    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10475    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10476    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10477    let multi_buffer_editor = cx.new_view(|cx| {
10478        Editor::new(
10479            EditorMode::Full,
10480            multi_buffer,
10481            Some(project.clone()),
10482            true,
10483            cx,
10484        )
10485    });
10486    let multibuffer_item_id = workspace
10487        .update(cx, |workspace, cx| {
10488            assert!(
10489                workspace.active_item(cx).is_none(),
10490                "active item should be None before the first item is added"
10491            );
10492            workspace.add_item_to_active_pane(
10493                Box::new(multi_buffer_editor.clone()),
10494                None,
10495                true,
10496                cx,
10497            );
10498            let active_item = workspace
10499                .active_item(cx)
10500                .expect("should have an active item after adding the multi buffer");
10501            assert!(
10502                !active_item.is_singleton(cx),
10503                "A multi buffer was expected to active after adding"
10504            );
10505            active_item.item_id()
10506        })
10507        .unwrap();
10508    cx.executor().run_until_parked();
10509
10510    multi_buffer_editor.update(cx, |editor, cx| {
10511        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10512        editor.open_excerpts(&OpenExcerpts, cx);
10513    });
10514    cx.executor().run_until_parked();
10515    let first_item_id = workspace
10516        .update(cx, |workspace, cx| {
10517            let active_item = workspace
10518                .active_item(cx)
10519                .expect("should have an active item after navigating into the 1st buffer");
10520            let first_item_id = active_item.item_id();
10521            assert_ne!(
10522                first_item_id, multibuffer_item_id,
10523                "Should navigate into the 1st buffer and activate it"
10524            );
10525            assert!(
10526                active_item.is_singleton(cx),
10527                "New active item should be a singleton buffer"
10528            );
10529            assert_eq!(
10530                active_item
10531                    .act_as::<Editor>(cx)
10532                    .expect("should have navigated into an editor for the 1st buffer")
10533                    .read(cx)
10534                    .text(cx),
10535                sample_text_1
10536            );
10537
10538            workspace
10539                .go_back(workspace.active_pane().downgrade(), cx)
10540                .detach_and_log_err(cx);
10541
10542            first_item_id
10543        })
10544        .unwrap();
10545    cx.executor().run_until_parked();
10546    workspace
10547        .update(cx, |workspace, cx| {
10548            let active_item = workspace
10549                .active_item(cx)
10550                .expect("should have an active item after navigating back");
10551            assert_eq!(
10552                active_item.item_id(),
10553                multibuffer_item_id,
10554                "Should navigate back to the multi buffer"
10555            );
10556            assert!(!active_item.is_singleton(cx));
10557        })
10558        .unwrap();
10559
10560    multi_buffer_editor.update(cx, |editor, cx| {
10561        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10562            s.select_ranges(Some(39..40))
10563        });
10564        editor.open_excerpts(&OpenExcerpts, cx);
10565    });
10566    cx.executor().run_until_parked();
10567    let second_item_id = workspace
10568        .update(cx, |workspace, cx| {
10569            let active_item = workspace
10570                .active_item(cx)
10571                .expect("should have an active item after navigating into the 2nd buffer");
10572            let second_item_id = active_item.item_id();
10573            assert_ne!(
10574                second_item_id, multibuffer_item_id,
10575                "Should navigate away from the multibuffer"
10576            );
10577            assert_ne!(
10578                second_item_id, first_item_id,
10579                "Should navigate into the 2nd buffer and activate it"
10580            );
10581            assert!(
10582                active_item.is_singleton(cx),
10583                "New active item should be a singleton buffer"
10584            );
10585            assert_eq!(
10586                active_item
10587                    .act_as::<Editor>(cx)
10588                    .expect("should have navigated into an editor")
10589                    .read(cx)
10590                    .text(cx),
10591                sample_text_2
10592            );
10593
10594            workspace
10595                .go_back(workspace.active_pane().downgrade(), cx)
10596                .detach_and_log_err(cx);
10597
10598            second_item_id
10599        })
10600        .unwrap();
10601    cx.executor().run_until_parked();
10602    workspace
10603        .update(cx, |workspace, cx| {
10604            let active_item = workspace
10605                .active_item(cx)
10606                .expect("should have an active item after navigating back from the 2nd buffer");
10607            assert_eq!(
10608                active_item.item_id(),
10609                multibuffer_item_id,
10610                "Should navigate back from the 2nd buffer to the multi buffer"
10611            );
10612            assert!(!active_item.is_singleton(cx));
10613        })
10614        .unwrap();
10615
10616    multi_buffer_editor.update(cx, |editor, cx| {
10617        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10618            s.select_ranges(Some(60..70))
10619        });
10620        editor.open_excerpts(&OpenExcerpts, cx);
10621    });
10622    cx.executor().run_until_parked();
10623    workspace
10624        .update(cx, |workspace, cx| {
10625            let active_item = workspace
10626                .active_item(cx)
10627                .expect("should have an active item after navigating into the 3rd buffer");
10628            let third_item_id = active_item.item_id();
10629            assert_ne!(
10630                third_item_id, multibuffer_item_id,
10631                "Should navigate into the 3rd buffer and activate it"
10632            );
10633            assert_ne!(third_item_id, first_item_id);
10634            assert_ne!(third_item_id, second_item_id);
10635            assert!(
10636                active_item.is_singleton(cx),
10637                "New active item should be a singleton buffer"
10638            );
10639            assert_eq!(
10640                active_item
10641                    .act_as::<Editor>(cx)
10642                    .expect("should have navigated into an editor")
10643                    .read(cx)
10644                    .text(cx),
10645                sample_text_3
10646            );
10647
10648            workspace
10649                .go_back(workspace.active_pane().downgrade(), cx)
10650                .detach_and_log_err(cx);
10651        })
10652        .unwrap();
10653    cx.executor().run_until_parked();
10654    workspace
10655        .update(cx, |workspace, cx| {
10656            let active_item = workspace
10657                .active_item(cx)
10658                .expect("should have an active item after navigating back from the 3rd buffer");
10659            assert_eq!(
10660                active_item.item_id(),
10661                multibuffer_item_id,
10662                "Should navigate back from the 3rd buffer to the multi buffer"
10663            );
10664            assert!(!active_item.is_singleton(cx));
10665        })
10666        .unwrap();
10667}
10668
10669#[gpui::test]
10670async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10671    init_test(cx, |_| {});
10672
10673    let mut cx = EditorTestContext::new(cx).await;
10674
10675    let diff_base = r#"
10676        use some::mod;
10677
10678        const A: u32 = 42;
10679
10680        fn main() {
10681            println!("hello");
10682
10683            println!("world");
10684        }
10685        "#
10686    .unindent();
10687
10688    cx.set_state(
10689        &r#"
10690        use some::modified;
10691
10692        ˇ
10693        fn main() {
10694            println!("hello there");
10695
10696            println!("around the");
10697            println!("world");
10698        }
10699        "#
10700        .unindent(),
10701    );
10702
10703    cx.set_diff_base(Some(&diff_base));
10704    executor.run_until_parked();
10705    let unexpanded_hunks = vec![
10706        (
10707            "use some::mod;\n".to_string(),
10708            DiffHunkStatus::Modified,
10709            DisplayRow(0)..DisplayRow(1),
10710        ),
10711        (
10712            "const A: u32 = 42;\n".to_string(),
10713            DiffHunkStatus::Removed,
10714            DisplayRow(2)..DisplayRow(2),
10715        ),
10716        (
10717            "    println!(\"hello\");\n".to_string(),
10718            DiffHunkStatus::Modified,
10719            DisplayRow(4)..DisplayRow(5),
10720        ),
10721        (
10722            "".to_string(),
10723            DiffHunkStatus::Added,
10724            DisplayRow(6)..DisplayRow(7),
10725        ),
10726    ];
10727    cx.update_editor(|editor, cx| {
10728        let snapshot = editor.snapshot(cx);
10729        let all_hunks = editor_hunks(editor, &snapshot, cx);
10730        assert_eq!(all_hunks, unexpanded_hunks);
10731    });
10732
10733    cx.update_editor(|editor, cx| {
10734        for _ in 0..4 {
10735            editor.go_to_hunk(&GoToHunk, cx);
10736            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10737        }
10738    });
10739    executor.run_until_parked();
10740    cx.assert_editor_state(
10741        &r#"
10742        use some::modified;
10743
10744        ˇ
10745        fn main() {
10746            println!("hello there");
10747
10748            println!("around the");
10749            println!("world");
10750        }
10751        "#
10752        .unindent(),
10753    );
10754    cx.update_editor(|editor, cx| {
10755        let snapshot = editor.snapshot(cx);
10756        let all_hunks = editor_hunks(editor, &snapshot, cx);
10757        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10758        assert_eq!(
10759            expanded_hunks_background_highlights(editor, cx),
10760            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10761            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10762        );
10763        assert_eq!(
10764            all_hunks,
10765            vec![
10766                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10767                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10768                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10769                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10770            ],
10771            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10772            (from modified and removed hunks)"
10773        );
10774        assert_eq!(
10775            all_hunks, all_expanded_hunks,
10776            "Editor hunks should not change and all be expanded"
10777        );
10778    });
10779
10780    cx.update_editor(|editor, cx| {
10781        editor.cancel(&Cancel, cx);
10782
10783        let snapshot = editor.snapshot(cx);
10784        let all_hunks = editor_hunks(editor, &snapshot, cx);
10785        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10786        assert_eq!(
10787            expanded_hunks_background_highlights(editor, cx),
10788            Vec::new(),
10789            "After cancelling in editor, no git highlights should be left"
10790        );
10791        assert_eq!(
10792            all_expanded_hunks,
10793            Vec::new(),
10794            "After cancelling in editor, no hunks should be expanded"
10795        );
10796        assert_eq!(
10797            all_hunks, unexpanded_hunks,
10798            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10799        );
10800    });
10801}
10802
10803#[gpui::test]
10804async fn test_toggled_diff_base_change(
10805    executor: BackgroundExecutor,
10806    cx: &mut gpui::TestAppContext,
10807) {
10808    init_test(cx, |_| {});
10809
10810    let mut cx = EditorTestContext::new(cx).await;
10811
10812    let diff_base = r#"
10813        use some::mod1;
10814        use some::mod2;
10815
10816        const A: u32 = 42;
10817        const B: u32 = 42;
10818        const C: u32 = 42;
10819
10820        fn main(ˇ) {
10821            println!("hello");
10822
10823            println!("world");
10824        }
10825        "#
10826    .unindent();
10827
10828    cx.set_state(
10829        &r#"
10830        use some::mod2;
10831
10832        const A: u32 = 42;
10833        const C: u32 = 42;
10834
10835        fn main(ˇ) {
10836            //println!("hello");
10837
10838            println!("world");
10839            //
10840            //
10841        }
10842        "#
10843        .unindent(),
10844    );
10845
10846    cx.set_diff_base(Some(&diff_base));
10847    executor.run_until_parked();
10848    cx.update_editor(|editor, cx| {
10849        let snapshot = editor.snapshot(cx);
10850        let all_hunks = editor_hunks(editor, &snapshot, cx);
10851        assert_eq!(
10852            all_hunks,
10853            vec![
10854                (
10855                    "use some::mod1;\n".to_string(),
10856                    DiffHunkStatus::Removed,
10857                    DisplayRow(0)..DisplayRow(0)
10858                ),
10859                (
10860                    "const B: u32 = 42;\n".to_string(),
10861                    DiffHunkStatus::Removed,
10862                    DisplayRow(3)..DisplayRow(3)
10863                ),
10864                (
10865                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10866                    DiffHunkStatus::Modified,
10867                    DisplayRow(5)..DisplayRow(7)
10868                ),
10869                (
10870                    "".to_string(),
10871                    DiffHunkStatus::Added,
10872                    DisplayRow(9)..DisplayRow(11)
10873                ),
10874            ]
10875        );
10876    });
10877
10878    cx.update_editor(|editor, cx| {
10879        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10880    });
10881    executor.run_until_parked();
10882    cx.assert_editor_state(
10883        &r#"
10884        use some::mod2;
10885
10886        const A: u32 = 42;
10887        const C: u32 = 42;
10888
10889        fn main(ˇ) {
10890            //println!("hello");
10891
10892            println!("world");
10893            //
10894            //
10895        }
10896        "#
10897        .unindent(),
10898    );
10899    cx.update_editor(|editor, cx| {
10900        let snapshot = editor.snapshot(cx);
10901        let all_hunks = editor_hunks(editor, &snapshot, cx);
10902        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10903        assert_eq!(
10904            expanded_hunks_background_highlights(editor, cx),
10905            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10906            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10907        );
10908        assert_eq!(
10909            all_hunks,
10910            vec![
10911                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10912                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10913                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10914                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10915            ],
10916            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10917            (from modified and removed hunks)"
10918        );
10919        assert_eq!(
10920            all_hunks, all_expanded_hunks,
10921            "Editor hunks should not change and all be expanded"
10922        );
10923    });
10924
10925    cx.set_diff_base(Some("new diff base!"));
10926    executor.run_until_parked();
10927
10928    cx.update_editor(|editor, cx| {
10929        let snapshot = editor.snapshot(cx);
10930        let all_hunks = editor_hunks(editor, &snapshot, cx);
10931        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10932        assert_eq!(
10933            expanded_hunks_background_highlights(editor, cx),
10934            Vec::new(),
10935            "After diff base is changed, old git highlights should be removed"
10936        );
10937        assert_eq!(
10938            all_expanded_hunks,
10939            Vec::new(),
10940            "After diff base is changed, old git hunk expansions should be removed"
10941        );
10942        assert_eq!(
10943            all_hunks,
10944            vec![(
10945                "new diff base!".to_string(),
10946                DiffHunkStatus::Modified,
10947                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10948            )],
10949            "After diff base is changed, hunks should update"
10950        );
10951    });
10952}
10953
10954#[gpui::test]
10955async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10956    init_test(cx, |_| {});
10957
10958    let mut cx = EditorTestContext::new(cx).await;
10959
10960    let diff_base = r#"
10961        use some::mod1;
10962        use some::mod2;
10963
10964        const A: u32 = 42;
10965        const B: u32 = 42;
10966        const C: u32 = 42;
10967
10968        fn main(ˇ) {
10969            println!("hello");
10970
10971            println!("world");
10972        }
10973
10974        fn another() {
10975            println!("another");
10976        }
10977
10978        fn another2() {
10979            println!("another2");
10980        }
10981        "#
10982    .unindent();
10983
10984    cx.set_state(
10985        &r#"
10986        «use some::mod2;
10987
10988        const A: u32 = 42;
10989        const C: u32 = 42;
10990
10991        fn main() {
10992            //println!("hello");
10993
10994            println!("world");
10995            //
10996            //ˇ»
10997        }
10998
10999        fn another() {
11000            println!("another");
11001            println!("another");
11002        }
11003
11004            println!("another2");
11005        }
11006        "#
11007        .unindent(),
11008    );
11009
11010    cx.set_diff_base(Some(&diff_base));
11011    executor.run_until_parked();
11012    cx.update_editor(|editor, cx| {
11013        let snapshot = editor.snapshot(cx);
11014        let all_hunks = editor_hunks(editor, &snapshot, cx);
11015        assert_eq!(
11016            all_hunks,
11017            vec![
11018                (
11019                    "use some::mod1;\n".to_string(),
11020                    DiffHunkStatus::Removed,
11021                    DisplayRow(0)..DisplayRow(0)
11022                ),
11023                (
11024                    "const B: u32 = 42;\n".to_string(),
11025                    DiffHunkStatus::Removed,
11026                    DisplayRow(3)..DisplayRow(3)
11027                ),
11028                (
11029                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11030                    DiffHunkStatus::Modified,
11031                    DisplayRow(5)..DisplayRow(7)
11032                ),
11033                (
11034                    "".to_string(),
11035                    DiffHunkStatus::Added,
11036                    DisplayRow(9)..DisplayRow(11)
11037                ),
11038                (
11039                    "".to_string(),
11040                    DiffHunkStatus::Added,
11041                    DisplayRow(15)..DisplayRow(16)
11042                ),
11043                (
11044                    "fn another2() {\n".to_string(),
11045                    DiffHunkStatus::Removed,
11046                    DisplayRow(18)..DisplayRow(18)
11047                ),
11048            ]
11049        );
11050    });
11051
11052    cx.update_editor(|editor, cx| {
11053        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11054    });
11055    executor.run_until_parked();
11056    cx.assert_editor_state(
11057        &r#"
11058        «use some::mod2;
11059
11060        const A: u32 = 42;
11061        const C: u32 = 42;
11062
11063        fn main() {
11064            //println!("hello");
11065
11066            println!("world");
11067            //
11068            //ˇ»
11069        }
11070
11071        fn another() {
11072            println!("another");
11073            println!("another");
11074        }
11075
11076            println!("another2");
11077        }
11078        "#
11079        .unindent(),
11080    );
11081    cx.update_editor(|editor, cx| {
11082        let snapshot = editor.snapshot(cx);
11083        let all_hunks = editor_hunks(editor, &snapshot, cx);
11084        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11085        assert_eq!(
11086            expanded_hunks_background_highlights(editor, cx),
11087            vec![
11088                DisplayRow(9)..=DisplayRow(10),
11089                DisplayRow(13)..=DisplayRow(14),
11090                DisplayRow(19)..=DisplayRow(19)
11091            ]
11092        );
11093        assert_eq!(
11094            all_hunks,
11095            vec![
11096                (
11097                    "use some::mod1;\n".to_string(),
11098                    DiffHunkStatus::Removed,
11099                    DisplayRow(1)..DisplayRow(1)
11100                ),
11101                (
11102                    "const B: u32 = 42;\n".to_string(),
11103                    DiffHunkStatus::Removed,
11104                    DisplayRow(5)..DisplayRow(5)
11105                ),
11106                (
11107                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11108                    DiffHunkStatus::Modified,
11109                    DisplayRow(9)..DisplayRow(11)
11110                ),
11111                (
11112                    "".to_string(),
11113                    DiffHunkStatus::Added,
11114                    DisplayRow(13)..DisplayRow(15)
11115                ),
11116                (
11117                    "".to_string(),
11118                    DiffHunkStatus::Added,
11119                    DisplayRow(19)..DisplayRow(20)
11120                ),
11121                (
11122                    "fn another2() {\n".to_string(),
11123                    DiffHunkStatus::Removed,
11124                    DisplayRow(23)..DisplayRow(23)
11125                ),
11126            ],
11127        );
11128        assert_eq!(all_hunks, all_expanded_hunks);
11129    });
11130
11131    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11132    cx.executor().run_until_parked();
11133    cx.assert_editor_state(
11134        &r#"
11135        «use some::mod2;
11136
11137        const A: u32 = 42;
11138        const C: u32 = 42;
11139
11140        fn main() {
11141            //println!("hello");
11142
11143            println!("world");
11144            //
11145            //ˇ»
11146        }
11147
11148        fn another() {
11149            println!("another");
11150            println!("another");
11151        }
11152
11153            println!("another2");
11154        }
11155        "#
11156        .unindent(),
11157    );
11158    cx.update_editor(|editor, cx| {
11159        let snapshot = editor.snapshot(cx);
11160        let all_hunks = editor_hunks(editor, &snapshot, cx);
11161        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11162        assert_eq!(
11163            expanded_hunks_background_highlights(editor, cx),
11164            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11165            "Only one hunk is left not folded, its highlight should be visible"
11166        );
11167        assert_eq!(
11168            all_hunks,
11169            vec![
11170                (
11171                    "use some::mod1;\n".to_string(),
11172                    DiffHunkStatus::Removed,
11173                    DisplayRow(0)..DisplayRow(0)
11174                ),
11175                (
11176                    "const B: u32 = 42;\n".to_string(),
11177                    DiffHunkStatus::Removed,
11178                    DisplayRow(0)..DisplayRow(0)
11179                ),
11180                (
11181                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11182                    DiffHunkStatus::Modified,
11183                    DisplayRow(0)..DisplayRow(0)
11184                ),
11185                (
11186                    "".to_string(),
11187                    DiffHunkStatus::Added,
11188                    DisplayRow(0)..DisplayRow(1)
11189                ),
11190                (
11191                    "".to_string(),
11192                    DiffHunkStatus::Added,
11193                    DisplayRow(5)..DisplayRow(6)
11194                ),
11195                (
11196                    "fn another2() {\n".to_string(),
11197                    DiffHunkStatus::Removed,
11198                    DisplayRow(9)..DisplayRow(9)
11199                ),
11200            ],
11201            "Hunk list should still return shifted folded hunks"
11202        );
11203        assert_eq!(
11204            all_expanded_hunks,
11205            vec![
11206                (
11207                    "".to_string(),
11208                    DiffHunkStatus::Added,
11209                    DisplayRow(5)..DisplayRow(6)
11210                ),
11211                (
11212                    "fn another2() {\n".to_string(),
11213                    DiffHunkStatus::Removed,
11214                    DisplayRow(9)..DisplayRow(9)
11215                ),
11216            ],
11217            "Only non-folded hunks should be left expanded"
11218        );
11219    });
11220
11221    cx.update_editor(|editor, cx| {
11222        editor.select_all(&SelectAll, cx);
11223        editor.unfold_lines(&UnfoldLines, cx);
11224    });
11225    cx.executor().run_until_parked();
11226    cx.assert_editor_state(
11227        &r#"
11228        «use some::mod2;
11229
11230        const A: u32 = 42;
11231        const C: u32 = 42;
11232
11233        fn main() {
11234            //println!("hello");
11235
11236            println!("world");
11237            //
11238            //
11239        }
11240
11241        fn another() {
11242            println!("another");
11243            println!("another");
11244        }
11245
11246            println!("another2");
11247        }
11248        ˇ»"#
11249        .unindent(),
11250    );
11251    cx.update_editor(|editor, cx| {
11252        let snapshot = editor.snapshot(cx);
11253        let all_hunks = editor_hunks(editor, &snapshot, cx);
11254        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11255        assert_eq!(
11256            expanded_hunks_background_highlights(editor, cx),
11257            vec![
11258                DisplayRow(9)..=DisplayRow(10),
11259                DisplayRow(13)..=DisplayRow(14),
11260                DisplayRow(19)..=DisplayRow(19)
11261            ],
11262            "After unfolding, all hunk diffs should be visible again"
11263        );
11264        assert_eq!(
11265            all_hunks,
11266            vec![
11267                (
11268                    "use some::mod1;\n".to_string(),
11269                    DiffHunkStatus::Removed,
11270                    DisplayRow(1)..DisplayRow(1)
11271                ),
11272                (
11273                    "const B: u32 = 42;\n".to_string(),
11274                    DiffHunkStatus::Removed,
11275                    DisplayRow(5)..DisplayRow(5)
11276                ),
11277                (
11278                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11279                    DiffHunkStatus::Modified,
11280                    DisplayRow(9)..DisplayRow(11)
11281                ),
11282                (
11283                    "".to_string(),
11284                    DiffHunkStatus::Added,
11285                    DisplayRow(13)..DisplayRow(15)
11286                ),
11287                (
11288                    "".to_string(),
11289                    DiffHunkStatus::Added,
11290                    DisplayRow(19)..DisplayRow(20)
11291                ),
11292                (
11293                    "fn another2() {\n".to_string(),
11294                    DiffHunkStatus::Removed,
11295                    DisplayRow(23)..DisplayRow(23)
11296                ),
11297            ],
11298        );
11299        assert_eq!(all_hunks, all_expanded_hunks);
11300    });
11301}
11302
11303#[gpui::test]
11304async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11305    init_test(cx, |_| {});
11306
11307    let cols = 4;
11308    let rows = 10;
11309    let sample_text_1 = sample_text(rows, cols, 'a');
11310    assert_eq!(
11311        sample_text_1,
11312        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11313    );
11314    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11315    let sample_text_2 = sample_text(rows, cols, 'l');
11316    assert_eq!(
11317        sample_text_2,
11318        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11319    );
11320    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11321    let sample_text_3 = sample_text(rows, cols, 'v');
11322    assert_eq!(
11323        sample_text_3,
11324        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11325    );
11326    let modified_sample_text_3 =
11327        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11328    let buffer_1 = cx.new_model(|cx| {
11329        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11330        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11331        buffer
11332    });
11333    let buffer_2 = cx.new_model(|cx| {
11334        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11335        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11336        buffer
11337    });
11338    let buffer_3 = cx.new_model(|cx| {
11339        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11340        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11341        buffer
11342    });
11343
11344    let multi_buffer = cx.new_model(|cx| {
11345        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11346        multibuffer.push_excerpts(
11347            buffer_1.clone(),
11348            [
11349                ExcerptRange {
11350                    context: Point::new(0, 0)..Point::new(3, 0),
11351                    primary: None,
11352                },
11353                ExcerptRange {
11354                    context: Point::new(5, 0)..Point::new(7, 0),
11355                    primary: None,
11356                },
11357                ExcerptRange {
11358                    context: Point::new(9, 0)..Point::new(10, 4),
11359                    primary: None,
11360                },
11361            ],
11362            cx,
11363        );
11364        multibuffer.push_excerpts(
11365            buffer_2.clone(),
11366            [
11367                ExcerptRange {
11368                    context: Point::new(0, 0)..Point::new(3, 0),
11369                    primary: None,
11370                },
11371                ExcerptRange {
11372                    context: Point::new(5, 0)..Point::new(7, 0),
11373                    primary: None,
11374                },
11375                ExcerptRange {
11376                    context: Point::new(9, 0)..Point::new(10, 4),
11377                    primary: None,
11378                },
11379            ],
11380            cx,
11381        );
11382        multibuffer.push_excerpts(
11383            buffer_3.clone(),
11384            [
11385                ExcerptRange {
11386                    context: Point::new(0, 0)..Point::new(3, 0),
11387                    primary: None,
11388                },
11389                ExcerptRange {
11390                    context: Point::new(5, 0)..Point::new(7, 0),
11391                    primary: None,
11392                },
11393                ExcerptRange {
11394                    context: Point::new(9, 0)..Point::new(10, 4),
11395                    primary: None,
11396                },
11397            ],
11398            cx,
11399        );
11400        multibuffer
11401    });
11402
11403    let fs = FakeFs::new(cx.executor());
11404    fs.insert_tree(
11405        "/a",
11406        json!({
11407            "main.rs": modified_sample_text_1,
11408            "other.rs": modified_sample_text_2,
11409            "lib.rs": modified_sample_text_3,
11410        }),
11411    )
11412    .await;
11413
11414    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11415    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11416    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11417    let multi_buffer_editor = cx.new_view(|cx| {
11418        Editor::new(
11419            EditorMode::Full,
11420            multi_buffer,
11421            Some(project.clone()),
11422            true,
11423            cx,
11424        )
11425    });
11426    cx.executor().run_until_parked();
11427
11428    let expected_all_hunks = vec![
11429        (
11430            "bbbb\n".to_string(),
11431            DiffHunkStatus::Removed,
11432            DisplayRow(4)..DisplayRow(4),
11433        ),
11434        (
11435            "nnnn\n".to_string(),
11436            DiffHunkStatus::Modified,
11437            DisplayRow(21)..DisplayRow(22),
11438        ),
11439        (
11440            "".to_string(),
11441            DiffHunkStatus::Added,
11442            DisplayRow(41)..DisplayRow(42),
11443        ),
11444    ];
11445    let expected_all_hunks_shifted = vec![
11446        (
11447            "bbbb\n".to_string(),
11448            DiffHunkStatus::Removed,
11449            DisplayRow(5)..DisplayRow(5),
11450        ),
11451        (
11452            "nnnn\n".to_string(),
11453            DiffHunkStatus::Modified,
11454            DisplayRow(23)..DisplayRow(24),
11455        ),
11456        (
11457            "".to_string(),
11458            DiffHunkStatus::Added,
11459            DisplayRow(43)..DisplayRow(44),
11460        ),
11461    ];
11462
11463    multi_buffer_editor.update(cx, |editor, cx| {
11464        let snapshot = editor.snapshot(cx);
11465        let all_hunks = editor_hunks(editor, &snapshot, cx);
11466        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11467        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11468        assert_eq!(all_hunks, expected_all_hunks);
11469        assert_eq!(all_expanded_hunks, Vec::new());
11470    });
11471
11472    multi_buffer_editor.update(cx, |editor, cx| {
11473        editor.select_all(&SelectAll, cx);
11474        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11475    });
11476    cx.executor().run_until_parked();
11477    multi_buffer_editor.update(cx, |editor, cx| {
11478        let snapshot = editor.snapshot(cx);
11479        let all_hunks = editor_hunks(editor, &snapshot, cx);
11480        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11481        assert_eq!(
11482            expanded_hunks_background_highlights(editor, cx),
11483            vec![
11484                DisplayRow(23)..=DisplayRow(23),
11485                DisplayRow(43)..=DisplayRow(43)
11486            ],
11487        );
11488        assert_eq!(all_hunks, expected_all_hunks_shifted);
11489        assert_eq!(all_hunks, all_expanded_hunks);
11490    });
11491
11492    multi_buffer_editor.update(cx, |editor, cx| {
11493        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11494    });
11495    cx.executor().run_until_parked();
11496    multi_buffer_editor.update(cx, |editor, cx| {
11497        let snapshot = editor.snapshot(cx);
11498        let all_hunks = editor_hunks(editor, &snapshot, cx);
11499        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11500        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11501        assert_eq!(all_hunks, expected_all_hunks);
11502        assert_eq!(all_expanded_hunks, Vec::new());
11503    });
11504
11505    multi_buffer_editor.update(cx, |editor, cx| {
11506        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11507    });
11508    cx.executor().run_until_parked();
11509    multi_buffer_editor.update(cx, |editor, cx| {
11510        let snapshot = editor.snapshot(cx);
11511        let all_hunks = editor_hunks(editor, &snapshot, cx);
11512        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11513        assert_eq!(
11514            expanded_hunks_background_highlights(editor, cx),
11515            vec![
11516                DisplayRow(23)..=DisplayRow(23),
11517                DisplayRow(43)..=DisplayRow(43)
11518            ],
11519        );
11520        assert_eq!(all_hunks, expected_all_hunks_shifted);
11521        assert_eq!(all_hunks, all_expanded_hunks);
11522    });
11523
11524    multi_buffer_editor.update(cx, |editor, cx| {
11525        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11526    });
11527    cx.executor().run_until_parked();
11528    multi_buffer_editor.update(cx, |editor, cx| {
11529        let snapshot = editor.snapshot(cx);
11530        let all_hunks = editor_hunks(editor, &snapshot, cx);
11531        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11532        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11533        assert_eq!(all_hunks, expected_all_hunks);
11534        assert_eq!(all_expanded_hunks, Vec::new());
11535    });
11536}
11537
11538#[gpui::test]
11539async fn test_edits_around_toggled_additions(
11540    executor: BackgroundExecutor,
11541    cx: &mut gpui::TestAppContext,
11542) {
11543    init_test(cx, |_| {});
11544
11545    let mut cx = EditorTestContext::new(cx).await;
11546
11547    let diff_base = r#"
11548        use some::mod1;
11549        use some::mod2;
11550
11551        const A: u32 = 42;
11552
11553        fn main() {
11554            println!("hello");
11555
11556            println!("world");
11557        }
11558        "#
11559    .unindent();
11560    executor.run_until_parked();
11561    cx.set_state(
11562        &r#"
11563        use some::mod1;
11564        use some::mod2;
11565
11566        const A: u32 = 42;
11567        const B: u32 = 42;
11568        const C: u32 = 42;
11569        ˇ
11570
11571        fn main() {
11572            println!("hello");
11573
11574            println!("world");
11575        }
11576        "#
11577        .unindent(),
11578    );
11579
11580    cx.set_diff_base(Some(&diff_base));
11581    executor.run_until_parked();
11582    cx.update_editor(|editor, cx| {
11583        let snapshot = editor.snapshot(cx);
11584        let all_hunks = editor_hunks(editor, &snapshot, cx);
11585        assert_eq!(
11586            all_hunks,
11587            vec![(
11588                "".to_string(),
11589                DiffHunkStatus::Added,
11590                DisplayRow(4)..DisplayRow(7)
11591            )]
11592        );
11593    });
11594    cx.update_editor(|editor, cx| {
11595        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11596    });
11597    executor.run_until_parked();
11598    cx.assert_editor_state(
11599        &r#"
11600        use some::mod1;
11601        use some::mod2;
11602
11603        const A: u32 = 42;
11604        const B: u32 = 42;
11605        const C: u32 = 42;
11606        ˇ
11607
11608        fn main() {
11609            println!("hello");
11610
11611            println!("world");
11612        }
11613        "#
11614        .unindent(),
11615    );
11616    cx.update_editor(|editor, cx| {
11617        let snapshot = editor.snapshot(cx);
11618        let all_hunks = editor_hunks(editor, &snapshot, cx);
11619        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11620        assert_eq!(
11621            all_hunks,
11622            vec![(
11623                "".to_string(),
11624                DiffHunkStatus::Added,
11625                DisplayRow(4)..DisplayRow(7)
11626            )]
11627        );
11628        assert_eq!(
11629            expanded_hunks_background_highlights(editor, cx),
11630            vec![DisplayRow(4)..=DisplayRow(6)]
11631        );
11632        assert_eq!(all_hunks, all_expanded_hunks);
11633    });
11634
11635    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11636    executor.run_until_parked();
11637    cx.assert_editor_state(
11638        &r#"
11639        use some::mod1;
11640        use some::mod2;
11641
11642        const A: u32 = 42;
11643        const B: u32 = 42;
11644        const C: u32 = 42;
11645        const D: u32 = 42;
11646        ˇ
11647
11648        fn main() {
11649            println!("hello");
11650
11651            println!("world");
11652        }
11653        "#
11654        .unindent(),
11655    );
11656    cx.update_editor(|editor, cx| {
11657        let snapshot = editor.snapshot(cx);
11658        let all_hunks = editor_hunks(editor, &snapshot, cx);
11659        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11660        assert_eq!(
11661            all_hunks,
11662            vec![(
11663                "".to_string(),
11664                DiffHunkStatus::Added,
11665                DisplayRow(4)..DisplayRow(8)
11666            )]
11667        );
11668        assert_eq!(
11669            expanded_hunks_background_highlights(editor, cx),
11670            vec![DisplayRow(4)..=DisplayRow(6)],
11671            "Edited hunk should have one more line added"
11672        );
11673        assert_eq!(
11674            all_hunks, all_expanded_hunks,
11675            "Expanded hunk should also grow with the addition"
11676        );
11677    });
11678
11679    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11680    executor.run_until_parked();
11681    cx.assert_editor_state(
11682        &r#"
11683        use some::mod1;
11684        use some::mod2;
11685
11686        const A: u32 = 42;
11687        const B: u32 = 42;
11688        const C: u32 = 42;
11689        const D: u32 = 42;
11690        const E: u32 = 42;
11691        ˇ
11692
11693        fn main() {
11694            println!("hello");
11695
11696            println!("world");
11697        }
11698        "#
11699        .unindent(),
11700    );
11701    cx.update_editor(|editor, cx| {
11702        let snapshot = editor.snapshot(cx);
11703        let all_hunks = editor_hunks(editor, &snapshot, cx);
11704        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11705        assert_eq!(
11706            all_hunks,
11707            vec![(
11708                "".to_string(),
11709                DiffHunkStatus::Added,
11710                DisplayRow(4)..DisplayRow(9)
11711            )]
11712        );
11713        assert_eq!(
11714            expanded_hunks_background_highlights(editor, cx),
11715            vec![DisplayRow(4)..=DisplayRow(6)],
11716            "Edited hunk should have one more line added"
11717        );
11718        assert_eq!(all_hunks, all_expanded_hunks);
11719    });
11720
11721    cx.update_editor(|editor, cx| {
11722        editor.move_up(&MoveUp, cx);
11723        editor.delete_line(&DeleteLine, cx);
11724    });
11725    executor.run_until_parked();
11726    cx.assert_editor_state(
11727        &r#"
11728        use some::mod1;
11729        use some::mod2;
11730
11731        const A: u32 = 42;
11732        const B: u32 = 42;
11733        const C: u32 = 42;
11734        const D: u32 = 42;
11735        ˇ
11736
11737        fn main() {
11738            println!("hello");
11739
11740            println!("world");
11741        }
11742        "#
11743        .unindent(),
11744    );
11745    cx.update_editor(|editor, cx| {
11746        let snapshot = editor.snapshot(cx);
11747        let all_hunks = editor_hunks(editor, &snapshot, cx);
11748        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11749        assert_eq!(
11750            all_hunks,
11751            vec![(
11752                "".to_string(),
11753                DiffHunkStatus::Added,
11754                DisplayRow(4)..DisplayRow(8)
11755            )]
11756        );
11757        assert_eq!(
11758            expanded_hunks_background_highlights(editor, cx),
11759            vec![DisplayRow(4)..=DisplayRow(6)],
11760            "Deleting a line should shrint the hunk"
11761        );
11762        assert_eq!(
11763            all_hunks, all_expanded_hunks,
11764            "Expanded hunk should also shrink with the addition"
11765        );
11766    });
11767
11768    cx.update_editor(|editor, cx| {
11769        editor.move_up(&MoveUp, cx);
11770        editor.delete_line(&DeleteLine, cx);
11771        editor.move_up(&MoveUp, cx);
11772        editor.delete_line(&DeleteLine, cx);
11773        editor.move_up(&MoveUp, cx);
11774        editor.delete_line(&DeleteLine, cx);
11775    });
11776    executor.run_until_parked();
11777    cx.assert_editor_state(
11778        &r#"
11779        use some::mod1;
11780        use some::mod2;
11781
11782        const A: u32 = 42;
11783        ˇ
11784
11785        fn main() {
11786            println!("hello");
11787
11788            println!("world");
11789        }
11790        "#
11791        .unindent(),
11792    );
11793    cx.update_editor(|editor, cx| {
11794        let snapshot = editor.snapshot(cx);
11795        let all_hunks = editor_hunks(editor, &snapshot, cx);
11796        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11797        assert_eq!(
11798            all_hunks,
11799            vec![(
11800                "".to_string(),
11801                DiffHunkStatus::Added,
11802                DisplayRow(5)..DisplayRow(6)
11803            )]
11804        );
11805        assert_eq!(
11806            expanded_hunks_background_highlights(editor, cx),
11807            vec![DisplayRow(5)..=DisplayRow(5)]
11808        );
11809        assert_eq!(all_hunks, all_expanded_hunks);
11810    });
11811
11812    cx.update_editor(|editor, cx| {
11813        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11814        editor.delete_line(&DeleteLine, cx);
11815    });
11816    executor.run_until_parked();
11817    cx.assert_editor_state(
11818        &r#"
11819        ˇ
11820
11821        fn main() {
11822            println!("hello");
11823
11824            println!("world");
11825        }
11826        "#
11827        .unindent(),
11828    );
11829    cx.update_editor(|editor, cx| {
11830        let snapshot = editor.snapshot(cx);
11831        let all_hunks = editor_hunks(editor, &snapshot, cx);
11832        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11833        assert_eq!(
11834            all_hunks,
11835            vec![
11836                (
11837                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11838                    DiffHunkStatus::Removed,
11839                    DisplayRow(0)..DisplayRow(0)
11840                ),
11841                (
11842                    "const A: u32 = 42;\n".to_string(),
11843                    DiffHunkStatus::Removed,
11844                    DisplayRow(2)..DisplayRow(2)
11845                )
11846            ]
11847        );
11848        assert_eq!(
11849            expanded_hunks_background_highlights(editor, cx),
11850            Vec::new(),
11851            "Should close all stale expanded addition hunks"
11852        );
11853        assert_eq!(
11854            all_expanded_hunks,
11855            vec![(
11856                "const A: u32 = 42;\n".to_string(),
11857                DiffHunkStatus::Removed,
11858                DisplayRow(2)..DisplayRow(2)
11859            )],
11860            "Should open hunks that were adjacent to the stale addition one"
11861        );
11862    });
11863}
11864
11865#[gpui::test]
11866async fn test_edits_around_toggled_deletions(
11867    executor: BackgroundExecutor,
11868    cx: &mut gpui::TestAppContext,
11869) {
11870    init_test(cx, |_| {});
11871
11872    let mut cx = EditorTestContext::new(cx).await;
11873
11874    let diff_base = r#"
11875        use some::mod1;
11876        use some::mod2;
11877
11878        const A: u32 = 42;
11879        const B: u32 = 42;
11880        const C: u32 = 42;
11881
11882
11883        fn main() {
11884            println!("hello");
11885
11886            println!("world");
11887        }
11888        "#
11889    .unindent();
11890    executor.run_until_parked();
11891    cx.set_state(
11892        &r#"
11893        use some::mod1;
11894        use some::mod2;
11895
11896        ˇconst B: u32 = 42;
11897        const C: u32 = 42;
11898
11899
11900        fn main() {
11901            println!("hello");
11902
11903            println!("world");
11904        }
11905        "#
11906        .unindent(),
11907    );
11908
11909    cx.set_diff_base(Some(&diff_base));
11910    executor.run_until_parked();
11911    cx.update_editor(|editor, cx| {
11912        let snapshot = editor.snapshot(cx);
11913        let all_hunks = editor_hunks(editor, &snapshot, cx);
11914        assert_eq!(
11915            all_hunks,
11916            vec![(
11917                "const A: u32 = 42;\n".to_string(),
11918                DiffHunkStatus::Removed,
11919                DisplayRow(3)..DisplayRow(3)
11920            )]
11921        );
11922    });
11923    cx.update_editor(|editor, cx| {
11924        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11925    });
11926    executor.run_until_parked();
11927    cx.assert_editor_state(
11928        &r#"
11929        use some::mod1;
11930        use some::mod2;
11931
11932        ˇconst B: u32 = 42;
11933        const C: u32 = 42;
11934
11935
11936        fn main() {
11937            println!("hello");
11938
11939            println!("world");
11940        }
11941        "#
11942        .unindent(),
11943    );
11944    cx.update_editor(|editor, cx| {
11945        let snapshot = editor.snapshot(cx);
11946        let all_hunks = editor_hunks(editor, &snapshot, cx);
11947        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11948        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11949        assert_eq!(
11950            all_hunks,
11951            vec![(
11952                "const A: u32 = 42;\n".to_string(),
11953                DiffHunkStatus::Removed,
11954                DisplayRow(4)..DisplayRow(4)
11955            )]
11956        );
11957        assert_eq!(all_hunks, all_expanded_hunks);
11958    });
11959
11960    cx.update_editor(|editor, cx| {
11961        editor.delete_line(&DeleteLine, cx);
11962    });
11963    executor.run_until_parked();
11964    cx.assert_editor_state(
11965        &r#"
11966        use some::mod1;
11967        use some::mod2;
11968
11969        ˇconst C: u32 = 42;
11970
11971
11972        fn main() {
11973            println!("hello");
11974
11975            println!("world");
11976        }
11977        "#
11978        .unindent(),
11979    );
11980    cx.update_editor(|editor, cx| {
11981        let snapshot = editor.snapshot(cx);
11982        let all_hunks = editor_hunks(editor, &snapshot, cx);
11983        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11984        assert_eq!(
11985            expanded_hunks_background_highlights(editor, cx),
11986            Vec::new(),
11987            "Deleted hunks do not highlight current editor's background"
11988        );
11989        assert_eq!(
11990            all_hunks,
11991            vec![(
11992                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11993                DiffHunkStatus::Removed,
11994                DisplayRow(5)..DisplayRow(5)
11995            )]
11996        );
11997        assert_eq!(all_hunks, all_expanded_hunks);
11998    });
11999
12000    cx.update_editor(|editor, cx| {
12001        editor.delete_line(&DeleteLine, cx);
12002    });
12003    executor.run_until_parked();
12004    cx.assert_editor_state(
12005        &r#"
12006        use some::mod1;
12007        use some::mod2;
12008
12009        ˇ
12010
12011        fn main() {
12012            println!("hello");
12013
12014            println!("world");
12015        }
12016        "#
12017        .unindent(),
12018    );
12019    cx.update_editor(|editor, cx| {
12020        let snapshot = editor.snapshot(cx);
12021        let all_hunks = editor_hunks(editor, &snapshot, cx);
12022        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12023        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12024        assert_eq!(
12025            all_hunks,
12026            vec![(
12027                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12028                DiffHunkStatus::Removed,
12029                DisplayRow(6)..DisplayRow(6)
12030            )]
12031        );
12032        assert_eq!(all_hunks, all_expanded_hunks);
12033    });
12034
12035    cx.update_editor(|editor, cx| {
12036        editor.handle_input("replacement", cx);
12037    });
12038    executor.run_until_parked();
12039    cx.assert_editor_state(
12040        &r#"
12041        use some::mod1;
12042        use some::mod2;
12043
12044        replacementˇ
12045
12046        fn main() {
12047            println!("hello");
12048
12049            println!("world");
12050        }
12051        "#
12052        .unindent(),
12053    );
12054    cx.update_editor(|editor, cx| {
12055        let snapshot = editor.snapshot(cx);
12056        let all_hunks = editor_hunks(editor, &snapshot, cx);
12057        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12058        assert_eq!(
12059            all_hunks,
12060            vec![(
12061                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12062                DiffHunkStatus::Modified,
12063                DisplayRow(7)..DisplayRow(8)
12064            )]
12065        );
12066        assert_eq!(
12067            expanded_hunks_background_highlights(editor, cx),
12068            vec![DisplayRow(7)..=DisplayRow(7)],
12069            "Modified expanded hunks should display additions and highlight their background"
12070        );
12071        assert_eq!(all_hunks, all_expanded_hunks);
12072    });
12073}
12074
12075#[gpui::test]
12076async fn test_edits_around_toggled_modifications(
12077    executor: BackgroundExecutor,
12078    cx: &mut gpui::TestAppContext,
12079) {
12080    init_test(cx, |_| {});
12081
12082    let mut cx = EditorTestContext::new(cx).await;
12083
12084    let diff_base = r#"
12085        use some::mod1;
12086        use some::mod2;
12087
12088        const A: u32 = 42;
12089        const B: u32 = 42;
12090        const C: u32 = 42;
12091        const D: u32 = 42;
12092
12093
12094        fn main() {
12095            println!("hello");
12096
12097            println!("world");
12098        }"#
12099    .unindent();
12100    executor.run_until_parked();
12101    cx.set_state(
12102        &r#"
12103        use some::mod1;
12104        use some::mod2;
12105
12106        const A: u32 = 42;
12107        const B: u32 = 42;
12108        const C: u32 = 43ˇ
12109        const D: u32 = 42;
12110
12111
12112        fn main() {
12113            println!("hello");
12114
12115            println!("world");
12116        }"#
12117        .unindent(),
12118    );
12119
12120    cx.set_diff_base(Some(&diff_base));
12121    executor.run_until_parked();
12122    cx.update_editor(|editor, cx| {
12123        let snapshot = editor.snapshot(cx);
12124        let all_hunks = editor_hunks(editor, &snapshot, cx);
12125        assert_eq!(
12126            all_hunks,
12127            vec![(
12128                "const C: u32 = 42;\n".to_string(),
12129                DiffHunkStatus::Modified,
12130                DisplayRow(5)..DisplayRow(6)
12131            )]
12132        );
12133    });
12134    cx.update_editor(|editor, cx| {
12135        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12136    });
12137    executor.run_until_parked();
12138    cx.assert_editor_state(
12139        &r#"
12140        use some::mod1;
12141        use some::mod2;
12142
12143        const A: u32 = 42;
12144        const B: u32 = 42;
12145        const C: u32 = 43ˇ
12146        const D: u32 = 42;
12147
12148
12149        fn main() {
12150            println!("hello");
12151
12152            println!("world");
12153        }"#
12154        .unindent(),
12155    );
12156    cx.update_editor(|editor, cx| {
12157        let snapshot = editor.snapshot(cx);
12158        let all_hunks = editor_hunks(editor, &snapshot, cx);
12159        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12160        assert_eq!(
12161            expanded_hunks_background_highlights(editor, cx),
12162            vec![DisplayRow(6)..=DisplayRow(6)],
12163        );
12164        assert_eq!(
12165            all_hunks,
12166            vec![(
12167                "const C: u32 = 42;\n".to_string(),
12168                DiffHunkStatus::Modified,
12169                DisplayRow(6)..DisplayRow(7)
12170            )]
12171        );
12172        assert_eq!(all_hunks, all_expanded_hunks);
12173    });
12174
12175    cx.update_editor(|editor, cx| {
12176        editor.handle_input("\nnew_line\n", cx);
12177    });
12178    executor.run_until_parked();
12179    cx.assert_editor_state(
12180        &r#"
12181            use some::mod1;
12182            use some::mod2;
12183
12184            const A: u32 = 42;
12185            const B: u32 = 42;
12186            const C: u32 = 43
12187            new_line
12188            ˇ
12189            const D: u32 = 42;
12190
12191
12192            fn main() {
12193                println!("hello");
12194
12195                println!("world");
12196            }"#
12197        .unindent(),
12198    );
12199    cx.update_editor(|editor, cx| {
12200        let snapshot = editor.snapshot(cx);
12201        let all_hunks = editor_hunks(editor, &snapshot, cx);
12202        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12203        assert_eq!(
12204            expanded_hunks_background_highlights(editor, cx),
12205            vec![DisplayRow(6)..=DisplayRow(6)],
12206            "Modified hunk should grow highlighted lines on more text additions"
12207        );
12208        assert_eq!(
12209            all_hunks,
12210            vec![(
12211                "const C: u32 = 42;\n".to_string(),
12212                DiffHunkStatus::Modified,
12213                DisplayRow(6)..DisplayRow(9)
12214            )]
12215        );
12216        assert_eq!(all_hunks, all_expanded_hunks);
12217    });
12218
12219    cx.update_editor(|editor, cx| {
12220        editor.move_up(&MoveUp, cx);
12221        editor.move_up(&MoveUp, cx);
12222        editor.move_up(&MoveUp, cx);
12223        editor.delete_line(&DeleteLine, cx);
12224    });
12225    executor.run_until_parked();
12226    cx.assert_editor_state(
12227        &r#"
12228            use some::mod1;
12229            use some::mod2;
12230
12231            const A: u32 = 42;
12232            ˇconst C: u32 = 43
12233            new_line
12234
12235            const D: u32 = 42;
12236
12237
12238            fn main() {
12239                println!("hello");
12240
12241                println!("world");
12242            }"#
12243        .unindent(),
12244    );
12245    cx.update_editor(|editor, cx| {
12246        let snapshot = editor.snapshot(cx);
12247        let all_hunks = editor_hunks(editor, &snapshot, cx);
12248        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12249        assert_eq!(
12250            expanded_hunks_background_highlights(editor, cx),
12251            vec![DisplayRow(6)..=DisplayRow(8)],
12252        );
12253        assert_eq!(
12254            all_hunks,
12255            vec![(
12256                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12257                DiffHunkStatus::Modified,
12258                DisplayRow(6)..DisplayRow(9)
12259            )],
12260            "Modified hunk should grow deleted lines on text deletions above"
12261        );
12262        assert_eq!(all_hunks, all_expanded_hunks);
12263    });
12264
12265    cx.update_editor(|editor, cx| {
12266        editor.move_up(&MoveUp, cx);
12267        editor.handle_input("v", cx);
12268    });
12269    executor.run_until_parked();
12270    cx.assert_editor_state(
12271        &r#"
12272            use some::mod1;
12273            use some::mod2;
12274
12275            vˇconst A: u32 = 42;
12276            const C: u32 = 43
12277            new_line
12278
12279            const D: u32 = 42;
12280
12281
12282            fn main() {
12283                println!("hello");
12284
12285                println!("world");
12286            }"#
12287        .unindent(),
12288    );
12289    cx.update_editor(|editor, cx| {
12290        let snapshot = editor.snapshot(cx);
12291        let all_hunks = editor_hunks(editor, &snapshot, cx);
12292        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12293        assert_eq!(
12294            expanded_hunks_background_highlights(editor, cx),
12295            vec![DisplayRow(6)..=DisplayRow(9)],
12296            "Modified hunk should grow deleted lines on text modifications above"
12297        );
12298        assert_eq!(
12299            all_hunks,
12300            vec![(
12301                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12302                DiffHunkStatus::Modified,
12303                DisplayRow(6)..DisplayRow(10)
12304            )]
12305        );
12306        assert_eq!(all_hunks, all_expanded_hunks);
12307    });
12308
12309    cx.update_editor(|editor, cx| {
12310        editor.move_down(&MoveDown, cx);
12311        editor.move_down(&MoveDown, cx);
12312        editor.delete_line(&DeleteLine, cx)
12313    });
12314    executor.run_until_parked();
12315    cx.assert_editor_state(
12316        &r#"
12317            use some::mod1;
12318            use some::mod2;
12319
12320            vconst A: u32 = 42;
12321            const C: u32 = 43
12322            ˇ
12323            const D: u32 = 42;
12324
12325
12326            fn main() {
12327                println!("hello");
12328
12329                println!("world");
12330            }"#
12331        .unindent(),
12332    );
12333    cx.update_editor(|editor, cx| {
12334        let snapshot = editor.snapshot(cx);
12335        let all_hunks = editor_hunks(editor, &snapshot, cx);
12336        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12337        assert_eq!(
12338            expanded_hunks_background_highlights(editor, cx),
12339            vec![DisplayRow(6)..=DisplayRow(8)],
12340            "Modified hunk should grow shrink lines on modification lines removal"
12341        );
12342        assert_eq!(
12343            all_hunks,
12344            vec![(
12345                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12346                DiffHunkStatus::Modified,
12347                DisplayRow(6)..DisplayRow(9)
12348            )]
12349        );
12350        assert_eq!(all_hunks, all_expanded_hunks);
12351    });
12352
12353    cx.update_editor(|editor, cx| {
12354        editor.move_up(&MoveUp, cx);
12355        editor.move_up(&MoveUp, cx);
12356        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12357        editor.delete_line(&DeleteLine, cx)
12358    });
12359    executor.run_until_parked();
12360    cx.assert_editor_state(
12361        &r#"
12362            use some::mod1;
12363            use some::mod2;
12364
12365            ˇ
12366
12367            fn main() {
12368                println!("hello");
12369
12370                println!("world");
12371            }"#
12372        .unindent(),
12373    );
12374    cx.update_editor(|editor, cx| {
12375        let snapshot = editor.snapshot(cx);
12376        let all_hunks = editor_hunks(editor, &snapshot, cx);
12377        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12378        assert_eq!(
12379            expanded_hunks_background_highlights(editor, cx),
12380            Vec::new(),
12381            "Modified hunk should turn into a removed one on all modified lines removal"
12382        );
12383        assert_eq!(
12384            all_hunks,
12385            vec![(
12386                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12387                    .to_string(),
12388                DiffHunkStatus::Removed,
12389                DisplayRow(7)..DisplayRow(7)
12390            )]
12391        );
12392        assert_eq!(all_hunks, all_expanded_hunks);
12393    });
12394}
12395
12396#[gpui::test]
12397async fn test_multiple_expanded_hunks_merge(
12398    executor: BackgroundExecutor,
12399    cx: &mut gpui::TestAppContext,
12400) {
12401    init_test(cx, |_| {});
12402
12403    let mut cx = EditorTestContext::new(cx).await;
12404
12405    let diff_base = r#"
12406        use some::mod1;
12407        use some::mod2;
12408
12409        const A: u32 = 42;
12410        const B: u32 = 42;
12411        const C: u32 = 42;
12412        const D: u32 = 42;
12413
12414
12415        fn main() {
12416            println!("hello");
12417
12418            println!("world");
12419        }"#
12420    .unindent();
12421    executor.run_until_parked();
12422    cx.set_state(
12423        &r#"
12424        use some::mod1;
12425        use some::mod2;
12426
12427        const A: u32 = 42;
12428        const B: u32 = 42;
12429        const C: u32 = 43ˇ
12430        const D: u32 = 42;
12431
12432
12433        fn main() {
12434            println!("hello");
12435
12436            println!("world");
12437        }"#
12438        .unindent(),
12439    );
12440
12441    cx.set_diff_base(Some(&diff_base));
12442    executor.run_until_parked();
12443    cx.update_editor(|editor, cx| {
12444        let snapshot = editor.snapshot(cx);
12445        let all_hunks = editor_hunks(editor, &snapshot, cx);
12446        assert_eq!(
12447            all_hunks,
12448            vec![(
12449                "const C: u32 = 42;\n".to_string(),
12450                DiffHunkStatus::Modified,
12451                DisplayRow(5)..DisplayRow(6)
12452            )]
12453        );
12454    });
12455    cx.update_editor(|editor, cx| {
12456        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12457    });
12458    executor.run_until_parked();
12459    cx.assert_editor_state(
12460        &r#"
12461        use some::mod1;
12462        use some::mod2;
12463
12464        const A: u32 = 42;
12465        const B: u32 = 42;
12466        const C: u32 = 43ˇ
12467        const D: u32 = 42;
12468
12469
12470        fn main() {
12471            println!("hello");
12472
12473            println!("world");
12474        }"#
12475        .unindent(),
12476    );
12477    cx.update_editor(|editor, cx| {
12478        let snapshot = editor.snapshot(cx);
12479        let all_hunks = editor_hunks(editor, &snapshot, cx);
12480        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12481        assert_eq!(
12482            expanded_hunks_background_highlights(editor, cx),
12483            vec![DisplayRow(6)..=DisplayRow(6)],
12484        );
12485        assert_eq!(
12486            all_hunks,
12487            vec![(
12488                "const C: u32 = 42;\n".to_string(),
12489                DiffHunkStatus::Modified,
12490                DisplayRow(6)..DisplayRow(7)
12491            )]
12492        );
12493        assert_eq!(all_hunks, all_expanded_hunks);
12494    });
12495
12496    cx.update_editor(|editor, cx| {
12497        editor.handle_input("\nnew_line\n", cx);
12498    });
12499    executor.run_until_parked();
12500    cx.assert_editor_state(
12501        &r#"
12502            use some::mod1;
12503            use some::mod2;
12504
12505            const A: u32 = 42;
12506            const B: u32 = 42;
12507            const C: u32 = 43
12508            new_line
12509            ˇ
12510            const D: u32 = 42;
12511
12512
12513            fn main() {
12514                println!("hello");
12515
12516                println!("world");
12517            }"#
12518        .unindent(),
12519    );
12520}
12521
12522async fn setup_indent_guides_editor(
12523    text: &str,
12524    cx: &mut gpui::TestAppContext,
12525) -> (BufferId, EditorTestContext) {
12526    init_test(cx, |_| {});
12527
12528    let mut cx = EditorTestContext::new(cx).await;
12529
12530    let buffer_id = cx.update_editor(|editor, cx| {
12531        editor.set_text(text, cx);
12532        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12533        let buffer_id = buffer_ids[0];
12534        buffer_id
12535    });
12536
12537    (buffer_id, cx)
12538}
12539
12540fn assert_indent_guides(
12541    range: Range<u32>,
12542    expected: Vec<IndentGuide>,
12543    active_indices: Option<Vec<usize>>,
12544    cx: &mut EditorTestContext,
12545) {
12546    let indent_guides = cx.update_editor(|editor, cx| {
12547        let snapshot = editor.snapshot(cx).display_snapshot;
12548        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12549            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12550            true,
12551            &snapshot,
12552            cx,
12553        );
12554
12555        indent_guides.sort_by(|a, b| {
12556            a.depth.cmp(&b.depth).then(
12557                a.start_row
12558                    .cmp(&b.start_row)
12559                    .then(a.end_row.cmp(&b.end_row)),
12560            )
12561        });
12562        indent_guides
12563    });
12564
12565    if let Some(expected) = active_indices {
12566        let active_indices = cx.update_editor(|editor, cx| {
12567            let snapshot = editor.snapshot(cx).display_snapshot;
12568            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12569        });
12570
12571        assert_eq!(
12572            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12573            expected,
12574            "Active indent guide indices do not match"
12575        );
12576    }
12577
12578    let expected: Vec<_> = expected
12579        .into_iter()
12580        .map(|guide| MultiBufferIndentGuide {
12581            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12582            buffer: guide,
12583        })
12584        .collect();
12585
12586    assert_eq!(indent_guides, expected, "Indent guides do not match");
12587}
12588
12589fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12590    IndentGuide {
12591        buffer_id,
12592        start_row,
12593        end_row,
12594        depth,
12595        tab_size: 4,
12596        settings: IndentGuideSettings {
12597            enabled: true,
12598            line_width: 1,
12599            active_line_width: 1,
12600            ..Default::default()
12601        },
12602    }
12603}
12604
12605#[gpui::test]
12606async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12607    let (buffer_id, mut cx) = setup_indent_guides_editor(
12608        &"
12609    fn main() {
12610        let a = 1;
12611    }"
12612        .unindent(),
12613        cx,
12614    )
12615    .await;
12616
12617    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12618}
12619
12620#[gpui::test]
12621async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12622    let (buffer_id, mut cx) = setup_indent_guides_editor(
12623        &"
12624    fn main() {
12625        let a = 1;
12626        let b = 2;
12627    }"
12628        .unindent(),
12629        cx,
12630    )
12631    .await;
12632
12633    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12634}
12635
12636#[gpui::test]
12637async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12638    let (buffer_id, mut cx) = setup_indent_guides_editor(
12639        &"
12640    fn main() {
12641        let a = 1;
12642        if a == 3 {
12643            let b = 2;
12644        } else {
12645            let c = 3;
12646        }
12647    }"
12648        .unindent(),
12649        cx,
12650    )
12651    .await;
12652
12653    assert_indent_guides(
12654        0..8,
12655        vec![
12656            indent_guide(buffer_id, 1, 6, 0),
12657            indent_guide(buffer_id, 3, 3, 1),
12658            indent_guide(buffer_id, 5, 5, 1),
12659        ],
12660        None,
12661        &mut cx,
12662    );
12663}
12664
12665#[gpui::test]
12666async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12667    let (buffer_id, mut cx) = setup_indent_guides_editor(
12668        &"
12669    fn main() {
12670        let a = 1;
12671            let b = 2;
12672        let c = 3;
12673    }"
12674        .unindent(),
12675        cx,
12676    )
12677    .await;
12678
12679    assert_indent_guides(
12680        0..5,
12681        vec![
12682            indent_guide(buffer_id, 1, 3, 0),
12683            indent_guide(buffer_id, 2, 2, 1),
12684        ],
12685        None,
12686        &mut cx,
12687    );
12688}
12689
12690#[gpui::test]
12691async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12692    let (buffer_id, mut cx) = setup_indent_guides_editor(
12693        &"
12694        fn main() {
12695            let a = 1;
12696
12697            let c = 3;
12698        }"
12699        .unindent(),
12700        cx,
12701    )
12702    .await;
12703
12704    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12705}
12706
12707#[gpui::test]
12708async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12709    let (buffer_id, mut cx) = setup_indent_guides_editor(
12710        &"
12711        fn main() {
12712            let a = 1;
12713
12714            let c = 3;
12715
12716            if a == 3 {
12717                let b = 2;
12718            } else {
12719                let c = 3;
12720            }
12721        }"
12722        .unindent(),
12723        cx,
12724    )
12725    .await;
12726
12727    assert_indent_guides(
12728        0..11,
12729        vec![
12730            indent_guide(buffer_id, 1, 9, 0),
12731            indent_guide(buffer_id, 6, 6, 1),
12732            indent_guide(buffer_id, 8, 8, 1),
12733        ],
12734        None,
12735        &mut cx,
12736    );
12737}
12738
12739#[gpui::test]
12740async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12741    let (buffer_id, mut cx) = setup_indent_guides_editor(
12742        &"
12743        fn main() {
12744            let a = 1;
12745
12746            let c = 3;
12747
12748            if a == 3 {
12749                let b = 2;
12750            } else {
12751                let c = 3;
12752            }
12753        }"
12754        .unindent(),
12755        cx,
12756    )
12757    .await;
12758
12759    assert_indent_guides(
12760        1..11,
12761        vec![
12762            indent_guide(buffer_id, 1, 9, 0),
12763            indent_guide(buffer_id, 6, 6, 1),
12764            indent_guide(buffer_id, 8, 8, 1),
12765        ],
12766        None,
12767        &mut cx,
12768    );
12769}
12770
12771#[gpui::test]
12772async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12773    let (buffer_id, mut cx) = setup_indent_guides_editor(
12774        &"
12775        fn main() {
12776            let a = 1;
12777
12778            let c = 3;
12779
12780            if a == 3 {
12781                let b = 2;
12782            } else {
12783                let c = 3;
12784            }
12785        }"
12786        .unindent(),
12787        cx,
12788    )
12789    .await;
12790
12791    assert_indent_guides(
12792        1..10,
12793        vec![
12794            indent_guide(buffer_id, 1, 9, 0),
12795            indent_guide(buffer_id, 6, 6, 1),
12796            indent_guide(buffer_id, 8, 8, 1),
12797        ],
12798        None,
12799        &mut cx,
12800    );
12801}
12802
12803#[gpui::test]
12804async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12805    let (buffer_id, mut cx) = setup_indent_guides_editor(
12806        &"
12807        block1
12808            block2
12809                block3
12810                    block4
12811            block2
12812        block1
12813        block1"
12814            .unindent(),
12815        cx,
12816    )
12817    .await;
12818
12819    assert_indent_guides(
12820        1..10,
12821        vec![
12822            indent_guide(buffer_id, 1, 4, 0),
12823            indent_guide(buffer_id, 2, 3, 1),
12824            indent_guide(buffer_id, 3, 3, 2),
12825        ],
12826        None,
12827        &mut cx,
12828    );
12829}
12830
12831#[gpui::test]
12832async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12833    let (buffer_id, mut cx) = setup_indent_guides_editor(
12834        &"
12835        block1
12836            block2
12837                block3
12838
12839        block1
12840        block1"
12841            .unindent(),
12842        cx,
12843    )
12844    .await;
12845
12846    assert_indent_guides(
12847        0..6,
12848        vec![
12849            indent_guide(buffer_id, 1, 2, 0),
12850            indent_guide(buffer_id, 2, 2, 1),
12851        ],
12852        None,
12853        &mut cx,
12854    );
12855}
12856
12857#[gpui::test]
12858async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12859    let (buffer_id, mut cx) = setup_indent_guides_editor(
12860        &"
12861        block1
12862
12863
12864
12865            block2
12866        "
12867        .unindent(),
12868        cx,
12869    )
12870    .await;
12871
12872    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12873}
12874
12875#[gpui::test]
12876async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12877    let (buffer_id, mut cx) = setup_indent_guides_editor(
12878        &"
12879        def a:
12880        \tb = 3
12881        \tif True:
12882        \t\tc = 4
12883        \t\td = 5
12884        \tprint(b)
12885        "
12886        .unindent(),
12887        cx,
12888    )
12889    .await;
12890
12891    assert_indent_guides(
12892        0..6,
12893        vec![
12894            indent_guide(buffer_id, 1, 6, 0),
12895            indent_guide(buffer_id, 3, 4, 1),
12896        ],
12897        None,
12898        &mut cx,
12899    );
12900}
12901
12902#[gpui::test]
12903async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12904    let (buffer_id, mut cx) = setup_indent_guides_editor(
12905        &"
12906    fn main() {
12907        let a = 1;
12908    }"
12909        .unindent(),
12910        cx,
12911    )
12912    .await;
12913
12914    cx.update_editor(|editor, cx| {
12915        editor.change_selections(None, cx, |s| {
12916            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12917        });
12918    });
12919
12920    assert_indent_guides(
12921        0..3,
12922        vec![indent_guide(buffer_id, 1, 1, 0)],
12923        Some(vec![0]),
12924        &mut cx,
12925    );
12926}
12927
12928#[gpui::test]
12929async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12930    let (buffer_id, mut cx) = setup_indent_guides_editor(
12931        &"
12932    fn main() {
12933        if 1 == 2 {
12934            let a = 1;
12935        }
12936    }"
12937        .unindent(),
12938        cx,
12939    )
12940    .await;
12941
12942    cx.update_editor(|editor, cx| {
12943        editor.change_selections(None, cx, |s| {
12944            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12945        });
12946    });
12947
12948    assert_indent_guides(
12949        0..4,
12950        vec![
12951            indent_guide(buffer_id, 1, 3, 0),
12952            indent_guide(buffer_id, 2, 2, 1),
12953        ],
12954        Some(vec![1]),
12955        &mut cx,
12956    );
12957
12958    cx.update_editor(|editor, cx| {
12959        editor.change_selections(None, cx, |s| {
12960            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12961        });
12962    });
12963
12964    assert_indent_guides(
12965        0..4,
12966        vec![
12967            indent_guide(buffer_id, 1, 3, 0),
12968            indent_guide(buffer_id, 2, 2, 1),
12969        ],
12970        Some(vec![1]),
12971        &mut cx,
12972    );
12973
12974    cx.update_editor(|editor, cx| {
12975        editor.change_selections(None, cx, |s| {
12976            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12977        });
12978    });
12979
12980    assert_indent_guides(
12981        0..4,
12982        vec![
12983            indent_guide(buffer_id, 1, 3, 0),
12984            indent_guide(buffer_id, 2, 2, 1),
12985        ],
12986        Some(vec![0]),
12987        &mut cx,
12988    );
12989}
12990
12991#[gpui::test]
12992async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12993    let (buffer_id, mut cx) = setup_indent_guides_editor(
12994        &"
12995    fn main() {
12996        let a = 1;
12997
12998        let b = 2;
12999    }"
13000        .unindent(),
13001        cx,
13002    )
13003    .await;
13004
13005    cx.update_editor(|editor, cx| {
13006        editor.change_selections(None, cx, |s| {
13007            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13008        });
13009    });
13010
13011    assert_indent_guides(
13012        0..5,
13013        vec![indent_guide(buffer_id, 1, 3, 0)],
13014        Some(vec![0]),
13015        &mut cx,
13016    );
13017}
13018
13019#[gpui::test]
13020async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13021    let (buffer_id, mut cx) = setup_indent_guides_editor(
13022        &"
13023    def m:
13024        a = 1
13025        pass"
13026            .unindent(),
13027        cx,
13028    )
13029    .await;
13030
13031    cx.update_editor(|editor, cx| {
13032        editor.change_selections(None, cx, |s| {
13033            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13034        });
13035    });
13036
13037    assert_indent_guides(
13038        0..3,
13039        vec![indent_guide(buffer_id, 1, 2, 0)],
13040        Some(vec![0]),
13041        &mut cx,
13042    );
13043}
13044
13045#[gpui::test]
13046fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13047    init_test(cx, |_| {});
13048
13049    let editor = cx.add_window(|cx| {
13050        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13051        build_editor(buffer, cx)
13052    });
13053
13054    let render_args = Arc::new(Mutex::new(None));
13055    let snapshot = editor
13056        .update(cx, |editor, cx| {
13057            let snapshot = editor.buffer().read(cx).snapshot(cx);
13058            let range =
13059                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13060
13061            struct RenderArgs {
13062                row: MultiBufferRow,
13063                folded: bool,
13064                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13065            }
13066
13067            let crease = Crease::new(
13068                range,
13069                FoldPlaceholder::test(),
13070                {
13071                    let toggle_callback = render_args.clone();
13072                    move |row, folded, callback, _cx| {
13073                        *toggle_callback.lock() = Some(RenderArgs {
13074                            row,
13075                            folded,
13076                            callback,
13077                        });
13078                        div()
13079                    }
13080                },
13081                |_row, _folded, _cx| div(),
13082            );
13083
13084            editor.insert_creases(Some(crease), cx);
13085            let snapshot = editor.snapshot(cx);
13086            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13087            snapshot
13088        })
13089        .unwrap();
13090
13091    let render_args = render_args.lock().take().unwrap();
13092    assert_eq!(render_args.row, MultiBufferRow(1));
13093    assert_eq!(render_args.folded, false);
13094    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13095
13096    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13097        .unwrap();
13098    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13099    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13100
13101    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13102        .unwrap();
13103    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13104    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13105}
13106
13107fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13108    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13109    point..point
13110}
13111
13112fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13113    let (text, ranges) = marked_text_ranges(marked_text, true);
13114    assert_eq!(view.text(cx), text);
13115    assert_eq!(
13116        view.selections.ranges(cx),
13117        ranges,
13118        "Assert selections are {}",
13119        marked_text
13120    );
13121}
13122
13123pub fn handle_signature_help_request(
13124    cx: &mut EditorLspTestContext,
13125    mocked_response: lsp::SignatureHelp,
13126) -> impl Future<Output = ()> {
13127    let mut request =
13128        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13129            let mocked_response = mocked_response.clone();
13130            async move { Ok(Some(mocked_response)) }
13131        });
13132
13133    async move {
13134        request.next().await;
13135    }
13136}
13137
13138/// Handle completion request passing a marked string specifying where the completion
13139/// should be triggered from using '|' character, what range should be replaced, and what completions
13140/// should be returned using '<' and '>' to delimit the range
13141pub fn handle_completion_request(
13142    cx: &mut EditorLspTestContext,
13143    marked_string: &str,
13144    completions: Vec<&'static str>,
13145    counter: Arc<AtomicUsize>,
13146) -> impl Future<Output = ()> {
13147    let complete_from_marker: TextRangeMarker = '|'.into();
13148    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13149    let (_, mut marked_ranges) = marked_text_ranges_by(
13150        marked_string,
13151        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13152    );
13153
13154    let complete_from_position =
13155        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13156    let replace_range =
13157        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13158
13159    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13160        let completions = completions.clone();
13161        counter.fetch_add(1, atomic::Ordering::Release);
13162        async move {
13163            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13164            assert_eq!(
13165                params.text_document_position.position,
13166                complete_from_position
13167            );
13168            Ok(Some(lsp::CompletionResponse::Array(
13169                completions
13170                    .iter()
13171                    .map(|completion_text| lsp::CompletionItem {
13172                        label: completion_text.to_string(),
13173                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13174                            range: replace_range,
13175                            new_text: completion_text.to_string(),
13176                        })),
13177                        ..Default::default()
13178                    })
13179                    .collect(),
13180            )))
13181        }
13182    });
13183
13184    async move {
13185        request.next().await;
13186    }
13187}
13188
13189fn handle_resolve_completion_request(
13190    cx: &mut EditorLspTestContext,
13191    edits: Option<Vec<(&'static str, &'static str)>>,
13192) -> impl Future<Output = ()> {
13193    let edits = edits.map(|edits| {
13194        edits
13195            .iter()
13196            .map(|(marked_string, new_text)| {
13197                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13198                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13199                lsp::TextEdit::new(replace_range, new_text.to_string())
13200            })
13201            .collect::<Vec<_>>()
13202    });
13203
13204    let mut request =
13205        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13206            let edits = edits.clone();
13207            async move {
13208                Ok(lsp::CompletionItem {
13209                    additional_text_edits: edits,
13210                    ..Default::default()
13211                })
13212            }
13213        });
13214
13215    async move {
13216        request.next().await;
13217    }
13218}
13219
13220pub(crate) fn update_test_language_settings(
13221    cx: &mut TestAppContext,
13222    f: impl Fn(&mut AllLanguageSettingsContent),
13223) {
13224    _ = cx.update(|cx| {
13225        SettingsStore::update_global(cx, |store, cx| {
13226            store.update_user_settings::<AllLanguageSettings>(cx, f);
13227        });
13228    });
13229}
13230
13231pub(crate) fn update_test_project_settings(
13232    cx: &mut TestAppContext,
13233    f: impl Fn(&mut ProjectSettings),
13234) {
13235    _ = cx.update(|cx| {
13236        SettingsStore::update_global(cx, |store, cx| {
13237            store.update_user_settings::<ProjectSettings>(cx, f);
13238        });
13239    });
13240}
13241
13242pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13243    _ = cx.update(|cx| {
13244        assets::Assets.load_test_fonts(cx);
13245        let store = SettingsStore::test(cx);
13246        cx.set_global(store);
13247        theme::init(theme::LoadThemes::JustBase, cx);
13248        release_channel::init(SemanticVersion::default(), cx);
13249        client::init_settings(cx);
13250        language::init(cx);
13251        Project::init_settings(cx);
13252        workspace::init_settings(cx);
13253        crate::init(cx);
13254    });
13255
13256    update_test_language_settings(cx, f);
13257}
13258
13259pub(crate) fn rust_lang() -> Arc<Language> {
13260    Arc::new(Language::new(
13261        LanguageConfig {
13262            name: "Rust".into(),
13263            matcher: LanguageMatcher {
13264                path_suffixes: vec!["rs".to_string()],
13265                ..Default::default()
13266            },
13267            ..Default::default()
13268        },
13269        Some(tree_sitter_rust::language()),
13270    ))
13271}
13272
13273#[track_caller]
13274fn assert_hunk_revert(
13275    not_reverted_text_with_selections: &str,
13276    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13277    expected_reverted_text_with_selections: &str,
13278    base_text: &str,
13279    cx: &mut EditorLspTestContext,
13280) {
13281    cx.set_state(not_reverted_text_with_selections);
13282    cx.update_editor(|editor, cx| {
13283        editor
13284            .buffer()
13285            .read(cx)
13286            .as_singleton()
13287            .unwrap()
13288            .update(cx, |buffer, cx| {
13289                buffer.set_diff_base(Some(base_text.into()), cx);
13290            });
13291    });
13292    cx.executor().run_until_parked();
13293
13294    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13295        let snapshot = editor.buffer().read(cx).snapshot(cx);
13296        let reverted_hunk_statuses = snapshot
13297            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13298            .map(|hunk| hunk_status(&hunk))
13299            .collect::<Vec<_>>();
13300
13301        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13302        reverted_hunk_statuses
13303    });
13304    cx.executor().run_until_parked();
13305    cx.assert_editor_state(expected_reverted_text_with_selections);
13306    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13307}