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::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 (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4720
 4721    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4722        .await;
 4723
 4724    _ = view.update(cx, |view, cx| {
 4725        view.change_selections(None, cx, |s| {
 4726            s.select_display_ranges([
 4727                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4728                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4729                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4730            ]);
 4731        });
 4732        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4733    });
 4734    assert_eq!(
 4735        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
 4736        &[
 4737            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4738            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4739            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4740        ]
 4741    );
 4742
 4743    _ = view.update(cx, |view, cx| {
 4744        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4745    });
 4746    assert_eq!(
 4747        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4748        &[
 4749            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4750            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4751        ]
 4752    );
 4753
 4754    _ = view.update(cx, |view, cx| {
 4755        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4756    });
 4757    assert_eq!(
 4758        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4759        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4760    );
 4761
 4762    // Trying to expand the selected syntax node one more time has no effect.
 4763    _ = view.update(cx, |view, cx| {
 4764        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4765    });
 4766    assert_eq!(
 4767        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4768        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4769    );
 4770
 4771    _ = view.update(cx, |view, cx| {
 4772        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4773    });
 4774    assert_eq!(
 4775        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4776        &[
 4777            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4778            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4779        ]
 4780    );
 4781
 4782    _ = view.update(cx, |view, cx| {
 4783        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4784    });
 4785    assert_eq!(
 4786        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4787        &[
 4788            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4789            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4790            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4791        ]
 4792    );
 4793
 4794    _ = view.update(cx, |view, cx| {
 4795        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4796    });
 4797    assert_eq!(
 4798        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4799        &[
 4800            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4801            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4802            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4803        ]
 4804    );
 4805
 4806    // Trying to shrink the selected syntax node one more time has no effect.
 4807    _ = view.update(cx, |view, cx| {
 4808        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4809    });
 4810    assert_eq!(
 4811        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4812        &[
 4813            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4814            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4815            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4816        ]
 4817    );
 4818
 4819    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4820    // a fold.
 4821    _ = view.update(cx, |view, cx| {
 4822        view.fold_ranges(
 4823            vec![
 4824                (
 4825                    Point::new(0, 21)..Point::new(0, 24),
 4826                    FoldPlaceholder::test(),
 4827                ),
 4828                (
 4829                    Point::new(3, 20)..Point::new(3, 22),
 4830                    FoldPlaceholder::test(),
 4831                ),
 4832            ],
 4833            true,
 4834            cx,
 4835        );
 4836        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4837    });
 4838    assert_eq!(
 4839        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4840        &[
 4841            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4842            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4843            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4844        ]
 4845    );
 4846}
 4847
 4848#[gpui::test]
 4849async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4850    init_test(cx, |_| {});
 4851
 4852    let language = Arc::new(
 4853        Language::new(
 4854            LanguageConfig {
 4855                brackets: BracketPairConfig {
 4856                    pairs: vec![
 4857                        BracketPair {
 4858                            start: "{".to_string(),
 4859                            end: "}".to_string(),
 4860                            close: false,
 4861                            surround: false,
 4862                            newline: true,
 4863                        },
 4864                        BracketPair {
 4865                            start: "(".to_string(),
 4866                            end: ")".to_string(),
 4867                            close: false,
 4868                            surround: false,
 4869                            newline: true,
 4870                        },
 4871                    ],
 4872                    ..Default::default()
 4873                },
 4874                ..Default::default()
 4875            },
 4876            Some(tree_sitter_rust::language()),
 4877        )
 4878        .with_indents_query(
 4879            r#"
 4880                (_ "(" ")" @end) @indent
 4881                (_ "{" "}" @end) @indent
 4882            "#,
 4883        )
 4884        .unwrap(),
 4885    );
 4886
 4887    let text = "fn a() {}";
 4888
 4889    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4890    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4891    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4892    editor
 4893        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4894        .await;
 4895
 4896    _ = editor.update(cx, |editor, cx| {
 4897        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4898        editor.newline(&Newline, cx);
 4899        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4900        assert_eq!(
 4901            editor.selections.ranges(cx),
 4902            &[
 4903                Point::new(1, 4)..Point::new(1, 4),
 4904                Point::new(3, 4)..Point::new(3, 4),
 4905                Point::new(5, 0)..Point::new(5, 0)
 4906            ]
 4907        );
 4908    });
 4909}
 4910
 4911#[gpui::test]
 4912async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4913    init_test(cx, |_| {});
 4914
 4915    let mut cx = EditorTestContext::new(cx).await;
 4916
 4917    let language = Arc::new(Language::new(
 4918        LanguageConfig {
 4919            brackets: BracketPairConfig {
 4920                pairs: vec![
 4921                    BracketPair {
 4922                        start: "{".to_string(),
 4923                        end: "}".to_string(),
 4924                        close: true,
 4925                        surround: true,
 4926                        newline: true,
 4927                    },
 4928                    BracketPair {
 4929                        start: "(".to_string(),
 4930                        end: ")".to_string(),
 4931                        close: true,
 4932                        surround: true,
 4933                        newline: true,
 4934                    },
 4935                    BracketPair {
 4936                        start: "/*".to_string(),
 4937                        end: " */".to_string(),
 4938                        close: true,
 4939                        surround: true,
 4940                        newline: true,
 4941                    },
 4942                    BracketPair {
 4943                        start: "[".to_string(),
 4944                        end: "]".to_string(),
 4945                        close: false,
 4946                        surround: false,
 4947                        newline: true,
 4948                    },
 4949                    BracketPair {
 4950                        start: "\"".to_string(),
 4951                        end: "\"".to_string(),
 4952                        close: true,
 4953                        surround: true,
 4954                        newline: false,
 4955                    },
 4956                    BracketPair {
 4957                        start: "<".to_string(),
 4958                        end: ">".to_string(),
 4959                        close: false,
 4960                        surround: true,
 4961                        newline: true,
 4962                    },
 4963                ],
 4964                ..Default::default()
 4965            },
 4966            autoclose_before: "})]".to_string(),
 4967            ..Default::default()
 4968        },
 4969        Some(tree_sitter_rust::language()),
 4970    ));
 4971
 4972    cx.language_registry().add(language.clone());
 4973    cx.update_buffer(|buffer, cx| {
 4974        buffer.set_language(Some(language), cx);
 4975    });
 4976
 4977    cx.set_state(
 4978        &r#"
 4979            🏀ˇ
 4980            εˇ
 4981            ❤️ˇ
 4982        "#
 4983        .unindent(),
 4984    );
 4985
 4986    // autoclose multiple nested brackets at multiple cursors
 4987    cx.update_editor(|view, cx| {
 4988        view.handle_input("{", cx);
 4989        view.handle_input("{", cx);
 4990        view.handle_input("{", cx);
 4991    });
 4992    cx.assert_editor_state(
 4993        &"
 4994            🏀{{{ˇ}}}
 4995            ε{{{ˇ}}}
 4996            ❤️{{{ˇ}}}
 4997        "
 4998        .unindent(),
 4999    );
 5000
 5001    // insert a different closing bracket
 5002    cx.update_editor(|view, cx| {
 5003        view.handle_input(")", cx);
 5004    });
 5005    cx.assert_editor_state(
 5006        &"
 5007            🏀{{{)ˇ}}}
 5008            ε{{{)ˇ}}}
 5009            ❤️{{{)ˇ}}}
 5010        "
 5011        .unindent(),
 5012    );
 5013
 5014    // skip over the auto-closed brackets when typing a closing bracket
 5015    cx.update_editor(|view, cx| {
 5016        view.move_right(&MoveRight, cx);
 5017        view.handle_input("}", cx);
 5018        view.handle_input("}", cx);
 5019        view.handle_input("}", cx);
 5020    });
 5021    cx.assert_editor_state(
 5022        &"
 5023            🏀{{{)}}}}ˇ
 5024            ε{{{)}}}}ˇ
 5025            ❤️{{{)}}}}ˇ
 5026        "
 5027        .unindent(),
 5028    );
 5029
 5030    // autoclose multi-character pairs
 5031    cx.set_state(
 5032        &"
 5033            ˇ
 5034            ˇ
 5035        "
 5036        .unindent(),
 5037    );
 5038    cx.update_editor(|view, cx| {
 5039        view.handle_input("/", cx);
 5040        view.handle_input("*", cx);
 5041    });
 5042    cx.assert_editor_state(
 5043        &"
 5044            /*ˇ */
 5045            /*ˇ */
 5046        "
 5047        .unindent(),
 5048    );
 5049
 5050    // one cursor autocloses a multi-character pair, one cursor
 5051    // does not autoclose.
 5052    cx.set_state(
 5053        &"
 5054 5055            ˇ
 5056        "
 5057        .unindent(),
 5058    );
 5059    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5060    cx.assert_editor_state(
 5061        &"
 5062            /*ˇ */
 5063 5064        "
 5065        .unindent(),
 5066    );
 5067
 5068    // Don't autoclose if the next character isn't whitespace and isn't
 5069    // listed in the language's "autoclose_before" section.
 5070    cx.set_state("ˇa b");
 5071    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5072    cx.assert_editor_state("{ˇa b");
 5073
 5074    // Don't autoclose if `close` is false for the bracket pair
 5075    cx.set_state("ˇ");
 5076    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5077    cx.assert_editor_state("");
 5078
 5079    // Surround with brackets if text is selected
 5080    cx.set_state("«aˇ» b");
 5081    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5082    cx.assert_editor_state("{«aˇ»} b");
 5083
 5084    // Autclose pair where the start and end characters are the same
 5085    cx.set_state("");
 5086    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5087    cx.assert_editor_state("a\"ˇ\"");
 5088    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5089    cx.assert_editor_state("a\"\"ˇ");
 5090
 5091    // Don't autoclose pair if autoclose is disabled
 5092    cx.set_state("ˇ");
 5093    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5094    cx.assert_editor_state("");
 5095
 5096    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5097    cx.set_state("«aˇ» b");
 5098    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5099    cx.assert_editor_state("<«aˇ»> b");
 5100}
 5101
 5102#[gpui::test]
 5103async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5104    init_test(cx, |settings| {
 5105        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5106    });
 5107
 5108    let mut cx = EditorTestContext::new(cx).await;
 5109
 5110    let language = Arc::new(Language::new(
 5111        LanguageConfig {
 5112            brackets: BracketPairConfig {
 5113                pairs: vec![
 5114                    BracketPair {
 5115                        start: "{".to_string(),
 5116                        end: "}".to_string(),
 5117                        close: true,
 5118                        surround: true,
 5119                        newline: true,
 5120                    },
 5121                    BracketPair {
 5122                        start: "(".to_string(),
 5123                        end: ")".to_string(),
 5124                        close: true,
 5125                        surround: true,
 5126                        newline: true,
 5127                    },
 5128                    BracketPair {
 5129                        start: "[".to_string(),
 5130                        end: "]".to_string(),
 5131                        close: false,
 5132                        surround: false,
 5133                        newline: true,
 5134                    },
 5135                ],
 5136                ..Default::default()
 5137            },
 5138            autoclose_before: "})]".to_string(),
 5139            ..Default::default()
 5140        },
 5141        Some(tree_sitter_rust::language()),
 5142    ));
 5143
 5144    cx.language_registry().add(language.clone());
 5145    cx.update_buffer(|buffer, cx| {
 5146        buffer.set_language(Some(language), cx);
 5147    });
 5148
 5149    cx.set_state(
 5150        &"
 5151            ˇ
 5152            ˇ
 5153            ˇ
 5154        "
 5155        .unindent(),
 5156    );
 5157
 5158    // ensure only matching closing brackets are skipped over
 5159    cx.update_editor(|view, cx| {
 5160        view.handle_input("}", cx);
 5161        view.move_left(&MoveLeft, cx);
 5162        view.handle_input(")", cx);
 5163        view.move_left(&MoveLeft, cx);
 5164    });
 5165    cx.assert_editor_state(
 5166        &"
 5167            ˇ)}
 5168            ˇ)}
 5169            ˇ)}
 5170        "
 5171        .unindent(),
 5172    );
 5173
 5174    // skip-over closing brackets at multiple cursors
 5175    cx.update_editor(|view, cx| {
 5176        view.handle_input(")", cx);
 5177        view.handle_input("}", cx);
 5178    });
 5179    cx.assert_editor_state(
 5180        &"
 5181            )}ˇ
 5182            )}ˇ
 5183            )}ˇ
 5184        "
 5185        .unindent(),
 5186    );
 5187
 5188    // ignore non-close brackets
 5189    cx.update_editor(|view, cx| {
 5190        view.handle_input("]", cx);
 5191        view.move_left(&MoveLeft, cx);
 5192        view.handle_input("]", cx);
 5193    });
 5194    cx.assert_editor_state(
 5195        &"
 5196            )}]ˇ]
 5197            )}]ˇ]
 5198            )}]ˇ]
 5199        "
 5200        .unindent(),
 5201    );
 5202}
 5203
 5204#[gpui::test]
 5205async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5206    init_test(cx, |_| {});
 5207
 5208    let mut cx = EditorTestContext::new(cx).await;
 5209
 5210    let html_language = Arc::new(
 5211        Language::new(
 5212            LanguageConfig {
 5213                name: "HTML".into(),
 5214                brackets: BracketPairConfig {
 5215                    pairs: vec![
 5216                        BracketPair {
 5217                            start: "<".into(),
 5218                            end: ">".into(),
 5219                            close: true,
 5220                            ..Default::default()
 5221                        },
 5222                        BracketPair {
 5223                            start: "{".into(),
 5224                            end: "}".into(),
 5225                            close: true,
 5226                            ..Default::default()
 5227                        },
 5228                        BracketPair {
 5229                            start: "(".into(),
 5230                            end: ")".into(),
 5231                            close: true,
 5232                            ..Default::default()
 5233                        },
 5234                    ],
 5235                    ..Default::default()
 5236                },
 5237                autoclose_before: "})]>".into(),
 5238                ..Default::default()
 5239            },
 5240            Some(tree_sitter_html::language()),
 5241        )
 5242        .with_injection_query(
 5243            r#"
 5244            (script_element
 5245                (raw_text) @content
 5246                (#set! "language" "javascript"))
 5247            "#,
 5248        )
 5249        .unwrap(),
 5250    );
 5251
 5252    let javascript_language = Arc::new(Language::new(
 5253        LanguageConfig {
 5254            name: "JavaScript".into(),
 5255            brackets: BracketPairConfig {
 5256                pairs: vec![
 5257                    BracketPair {
 5258                        start: "/*".into(),
 5259                        end: " */".into(),
 5260                        close: true,
 5261                        ..Default::default()
 5262                    },
 5263                    BracketPair {
 5264                        start: "{".into(),
 5265                        end: "}".into(),
 5266                        close: true,
 5267                        ..Default::default()
 5268                    },
 5269                    BracketPair {
 5270                        start: "(".into(),
 5271                        end: ")".into(),
 5272                        close: true,
 5273                        ..Default::default()
 5274                    },
 5275                ],
 5276                ..Default::default()
 5277            },
 5278            autoclose_before: "})]>".into(),
 5279            ..Default::default()
 5280        },
 5281        Some(tree_sitter_typescript::language_tsx()),
 5282    ));
 5283
 5284    cx.language_registry().add(html_language.clone());
 5285    cx.language_registry().add(javascript_language.clone());
 5286
 5287    cx.update_buffer(|buffer, cx| {
 5288        buffer.set_language(Some(html_language), cx);
 5289    });
 5290
 5291    cx.set_state(
 5292        &r#"
 5293            <body>ˇ
 5294                <script>
 5295                    var x = 1;ˇ
 5296                </script>
 5297            </body>ˇ
 5298        "#
 5299        .unindent(),
 5300    );
 5301
 5302    // Precondition: different languages are active at different locations.
 5303    cx.update_editor(|editor, cx| {
 5304        let snapshot = editor.snapshot(cx);
 5305        let cursors = editor.selections.ranges::<usize>(cx);
 5306        let languages = cursors
 5307            .iter()
 5308            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5309            .collect::<Vec<_>>();
 5310        assert_eq!(
 5311            languages,
 5312            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5313        );
 5314    });
 5315
 5316    // Angle brackets autoclose in HTML, but not JavaScript.
 5317    cx.update_editor(|editor, cx| {
 5318        editor.handle_input("<", cx);
 5319        editor.handle_input("a", cx);
 5320    });
 5321    cx.assert_editor_state(
 5322        &r#"
 5323            <body><aˇ>
 5324                <script>
 5325                    var x = 1;<aˇ
 5326                </script>
 5327            </body><aˇ>
 5328        "#
 5329        .unindent(),
 5330    );
 5331
 5332    // Curly braces and parens autoclose in both HTML and JavaScript.
 5333    cx.update_editor(|editor, cx| {
 5334        editor.handle_input(" b=", cx);
 5335        editor.handle_input("{", cx);
 5336        editor.handle_input("c", cx);
 5337        editor.handle_input("(", cx);
 5338    });
 5339    cx.assert_editor_state(
 5340        &r#"
 5341            <body><a b={c(ˇ)}>
 5342                <script>
 5343                    var x = 1;<a b={c(ˇ)}
 5344                </script>
 5345            </body><a b={c(ˇ)}>
 5346        "#
 5347        .unindent(),
 5348    );
 5349
 5350    // Brackets that were already autoclosed are skipped.
 5351    cx.update_editor(|editor, cx| {
 5352        editor.handle_input(")", cx);
 5353        editor.handle_input("d", cx);
 5354        editor.handle_input("}", cx);
 5355    });
 5356    cx.assert_editor_state(
 5357        &r#"
 5358            <body><a b={c()d}ˇ>
 5359                <script>
 5360                    var x = 1;<a b={c()d}ˇ
 5361                </script>
 5362            </body><a b={c()d}ˇ>
 5363        "#
 5364        .unindent(),
 5365    );
 5366    cx.update_editor(|editor, cx| {
 5367        editor.handle_input(">", cx);
 5368    });
 5369    cx.assert_editor_state(
 5370        &r#"
 5371            <body><a b={c()d}>ˇ
 5372                <script>
 5373                    var x = 1;<a b={c()d}>ˇ
 5374                </script>
 5375            </body><a b={c()d}>ˇ
 5376        "#
 5377        .unindent(),
 5378    );
 5379
 5380    // Reset
 5381    cx.set_state(
 5382        &r#"
 5383            <body>ˇ
 5384                <script>
 5385                    var x = 1;ˇ
 5386                </script>
 5387            </body>ˇ
 5388        "#
 5389        .unindent(),
 5390    );
 5391
 5392    cx.update_editor(|editor, cx| {
 5393        editor.handle_input("<", cx);
 5394    });
 5395    cx.assert_editor_state(
 5396        &r#"
 5397            <body><ˇ>
 5398                <script>
 5399                    var x = 1;<ˇ
 5400                </script>
 5401            </body><ˇ>
 5402        "#
 5403        .unindent(),
 5404    );
 5405
 5406    // When backspacing, the closing angle brackets are removed.
 5407    cx.update_editor(|editor, cx| {
 5408        editor.backspace(&Backspace, cx);
 5409    });
 5410    cx.assert_editor_state(
 5411        &r#"
 5412            <body>ˇ
 5413                <script>
 5414                    var x = 1;ˇ
 5415                </script>
 5416            </body>ˇ
 5417        "#
 5418        .unindent(),
 5419    );
 5420
 5421    // Block comments autoclose in JavaScript, but not HTML.
 5422    cx.update_editor(|editor, cx| {
 5423        editor.handle_input("/", cx);
 5424        editor.handle_input("*", cx);
 5425    });
 5426    cx.assert_editor_state(
 5427        &r#"
 5428            <body>/*ˇ
 5429                <script>
 5430                    var x = 1;/*ˇ */
 5431                </script>
 5432            </body>/*ˇ
 5433        "#
 5434        .unindent(),
 5435    );
 5436}
 5437
 5438#[gpui::test]
 5439async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5440    init_test(cx, |_| {});
 5441
 5442    let mut cx = EditorTestContext::new(cx).await;
 5443
 5444    let rust_language = Arc::new(
 5445        Language::new(
 5446            LanguageConfig {
 5447                name: "Rust".into(),
 5448                brackets: serde_json::from_value(json!([
 5449                    { "start": "{", "end": "}", "close": true, "newline": true },
 5450                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5451                ]))
 5452                .unwrap(),
 5453                autoclose_before: "})]>".into(),
 5454                ..Default::default()
 5455            },
 5456            Some(tree_sitter_rust::language()),
 5457        )
 5458        .with_override_query("(string_literal) @string")
 5459        .unwrap(),
 5460    );
 5461
 5462    cx.language_registry().add(rust_language.clone());
 5463    cx.update_buffer(|buffer, cx| {
 5464        buffer.set_language(Some(rust_language), cx);
 5465    });
 5466
 5467    cx.set_state(
 5468        &r#"
 5469            let x = ˇ
 5470        "#
 5471        .unindent(),
 5472    );
 5473
 5474    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5475    cx.update_editor(|editor, cx| {
 5476        editor.handle_input("\"", cx);
 5477    });
 5478    cx.assert_editor_state(
 5479        &r#"
 5480            let x = "ˇ"
 5481        "#
 5482        .unindent(),
 5483    );
 5484
 5485    // Inserting another quotation mark. The cursor moves across the existing
 5486    // automatically-inserted quotation mark.
 5487    cx.update_editor(|editor, cx| {
 5488        editor.handle_input("\"", cx);
 5489    });
 5490    cx.assert_editor_state(
 5491        &r#"
 5492            let x = ""ˇ
 5493        "#
 5494        .unindent(),
 5495    );
 5496
 5497    // Reset
 5498    cx.set_state(
 5499        &r#"
 5500            let x = ˇ
 5501        "#
 5502        .unindent(),
 5503    );
 5504
 5505    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5506    cx.update_editor(|editor, cx| {
 5507        editor.handle_input("\"", cx);
 5508        editor.handle_input(" ", cx);
 5509        editor.move_left(&Default::default(), cx);
 5510        editor.handle_input("\\", cx);
 5511        editor.handle_input("\"", cx);
 5512    });
 5513    cx.assert_editor_state(
 5514        &r#"
 5515            let x = "\"ˇ "
 5516        "#
 5517        .unindent(),
 5518    );
 5519
 5520    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5521    // mark. Nothing is inserted.
 5522    cx.update_editor(|editor, cx| {
 5523        editor.move_right(&Default::default(), cx);
 5524        editor.handle_input("\"", cx);
 5525    });
 5526    cx.assert_editor_state(
 5527        &r#"
 5528            let x = "\" "ˇ
 5529        "#
 5530        .unindent(),
 5531    );
 5532}
 5533
 5534#[gpui::test]
 5535async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5536    init_test(cx, |_| {});
 5537
 5538    let language = Arc::new(Language::new(
 5539        LanguageConfig {
 5540            brackets: BracketPairConfig {
 5541                pairs: vec![
 5542                    BracketPair {
 5543                        start: "{".to_string(),
 5544                        end: "}".to_string(),
 5545                        close: true,
 5546                        surround: true,
 5547                        newline: true,
 5548                    },
 5549                    BracketPair {
 5550                        start: "/* ".to_string(),
 5551                        end: "*/".to_string(),
 5552                        close: true,
 5553                        surround: true,
 5554                        ..Default::default()
 5555                    },
 5556                ],
 5557                ..Default::default()
 5558            },
 5559            ..Default::default()
 5560        },
 5561        Some(tree_sitter_rust::language()),
 5562    ));
 5563
 5564    let text = r#"
 5565        a
 5566        b
 5567        c
 5568    "#
 5569    .unindent();
 5570
 5571    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5572    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5573    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5574    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5575        .await;
 5576
 5577    _ = view.update(cx, |view, cx| {
 5578        view.change_selections(None, cx, |s| {
 5579            s.select_display_ranges([
 5580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5581                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5582                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5583            ])
 5584        });
 5585
 5586        view.handle_input("{", cx);
 5587        view.handle_input("{", cx);
 5588        view.handle_input("{", cx);
 5589        assert_eq!(
 5590            view.text(cx),
 5591            "
 5592                {{{a}}}
 5593                {{{b}}}
 5594                {{{c}}}
 5595            "
 5596            .unindent()
 5597        );
 5598        assert_eq!(
 5599            view.selections.display_ranges(cx),
 5600            [
 5601                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5602                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5603                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5604            ]
 5605        );
 5606
 5607        view.undo(&Undo, cx);
 5608        view.undo(&Undo, cx);
 5609        view.undo(&Undo, cx);
 5610        assert_eq!(
 5611            view.text(cx),
 5612            "
 5613                a
 5614                b
 5615                c
 5616            "
 5617            .unindent()
 5618        );
 5619        assert_eq!(
 5620            view.selections.display_ranges(cx),
 5621            [
 5622                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5623                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5624                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5625            ]
 5626        );
 5627
 5628        // Ensure inserting the first character of a multi-byte bracket pair
 5629        // doesn't surround the selections with the bracket.
 5630        view.handle_input("/", cx);
 5631        assert_eq!(
 5632            view.text(cx),
 5633            "
 5634                /
 5635                /
 5636                /
 5637            "
 5638            .unindent()
 5639        );
 5640        assert_eq!(
 5641            view.selections.display_ranges(cx),
 5642            [
 5643                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5644                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5645                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5646            ]
 5647        );
 5648
 5649        view.undo(&Undo, cx);
 5650        assert_eq!(
 5651            view.text(cx),
 5652            "
 5653                a
 5654                b
 5655                c
 5656            "
 5657            .unindent()
 5658        );
 5659        assert_eq!(
 5660            view.selections.display_ranges(cx),
 5661            [
 5662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5663                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5664                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5665            ]
 5666        );
 5667
 5668        // Ensure inserting the last character of a multi-byte bracket pair
 5669        // doesn't surround the selections with the bracket.
 5670        view.handle_input("*", cx);
 5671        assert_eq!(
 5672            view.text(cx),
 5673            "
 5674                *
 5675                *
 5676                *
 5677            "
 5678            .unindent()
 5679        );
 5680        assert_eq!(
 5681            view.selections.display_ranges(cx),
 5682            [
 5683                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5686            ]
 5687        );
 5688    });
 5689}
 5690
 5691#[gpui::test]
 5692async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5693    init_test(cx, |_| {});
 5694
 5695    let language = Arc::new(Language::new(
 5696        LanguageConfig {
 5697            brackets: BracketPairConfig {
 5698                pairs: vec![BracketPair {
 5699                    start: "{".to_string(),
 5700                    end: "}".to_string(),
 5701                    close: true,
 5702                    surround: true,
 5703                    newline: true,
 5704                }],
 5705                ..Default::default()
 5706            },
 5707            autoclose_before: "}".to_string(),
 5708            ..Default::default()
 5709        },
 5710        Some(tree_sitter_rust::language()),
 5711    ));
 5712
 5713    let text = r#"
 5714        a
 5715        b
 5716        c
 5717    "#
 5718    .unindent();
 5719
 5720    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5721    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5722    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5723    editor
 5724        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5725        .await;
 5726
 5727    _ = editor.update(cx, |editor, cx| {
 5728        editor.change_selections(None, cx, |s| {
 5729            s.select_ranges([
 5730                Point::new(0, 1)..Point::new(0, 1),
 5731                Point::new(1, 1)..Point::new(1, 1),
 5732                Point::new(2, 1)..Point::new(2, 1),
 5733            ])
 5734        });
 5735
 5736        editor.handle_input("{", cx);
 5737        editor.handle_input("{", cx);
 5738        editor.handle_input("_", cx);
 5739        assert_eq!(
 5740            editor.text(cx),
 5741            "
 5742                a{{_}}
 5743                b{{_}}
 5744                c{{_}}
 5745            "
 5746            .unindent()
 5747        );
 5748        assert_eq!(
 5749            editor.selections.ranges::<Point>(cx),
 5750            [
 5751                Point::new(0, 4)..Point::new(0, 4),
 5752                Point::new(1, 4)..Point::new(1, 4),
 5753                Point::new(2, 4)..Point::new(2, 4)
 5754            ]
 5755        );
 5756
 5757        editor.backspace(&Default::default(), cx);
 5758        editor.backspace(&Default::default(), cx);
 5759        assert_eq!(
 5760            editor.text(cx),
 5761            "
 5762                a{}
 5763                b{}
 5764                c{}
 5765            "
 5766            .unindent()
 5767        );
 5768        assert_eq!(
 5769            editor.selections.ranges::<Point>(cx),
 5770            [
 5771                Point::new(0, 2)..Point::new(0, 2),
 5772                Point::new(1, 2)..Point::new(1, 2),
 5773                Point::new(2, 2)..Point::new(2, 2)
 5774            ]
 5775        );
 5776
 5777        editor.delete_to_previous_word_start(&Default::default(), cx);
 5778        assert_eq!(
 5779            editor.text(cx),
 5780            "
 5781                a
 5782                b
 5783                c
 5784            "
 5785            .unindent()
 5786        );
 5787        assert_eq!(
 5788            editor.selections.ranges::<Point>(cx),
 5789            [
 5790                Point::new(0, 1)..Point::new(0, 1),
 5791                Point::new(1, 1)..Point::new(1, 1),
 5792                Point::new(2, 1)..Point::new(2, 1)
 5793            ]
 5794        );
 5795    });
 5796}
 5797
 5798#[gpui::test]
 5799async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5800    init_test(cx, |settings| {
 5801        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5802    });
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805
 5806    let language = Arc::new(Language::new(
 5807        LanguageConfig {
 5808            brackets: BracketPairConfig {
 5809                pairs: vec![
 5810                    BracketPair {
 5811                        start: "{".to_string(),
 5812                        end: "}".to_string(),
 5813                        close: true,
 5814                        surround: true,
 5815                        newline: true,
 5816                    },
 5817                    BracketPair {
 5818                        start: "(".to_string(),
 5819                        end: ")".to_string(),
 5820                        close: true,
 5821                        surround: true,
 5822                        newline: true,
 5823                    },
 5824                    BracketPair {
 5825                        start: "[".to_string(),
 5826                        end: "]".to_string(),
 5827                        close: false,
 5828                        surround: true,
 5829                        newline: true,
 5830                    },
 5831                ],
 5832                ..Default::default()
 5833            },
 5834            autoclose_before: "})]".to_string(),
 5835            ..Default::default()
 5836        },
 5837        Some(tree_sitter_rust::language()),
 5838    ));
 5839
 5840    cx.language_registry().add(language.clone());
 5841    cx.update_buffer(|buffer, cx| {
 5842        buffer.set_language(Some(language), cx);
 5843    });
 5844
 5845    cx.set_state(
 5846        &"
 5847            {(ˇ)}
 5848            [[ˇ]]
 5849            {(ˇ)}
 5850        "
 5851        .unindent(),
 5852    );
 5853
 5854    cx.update_editor(|view, cx| {
 5855        view.backspace(&Default::default(), cx);
 5856        view.backspace(&Default::default(), cx);
 5857    });
 5858
 5859    cx.assert_editor_state(
 5860        &"
 5861            ˇ
 5862            ˇ]]
 5863            ˇ
 5864        "
 5865        .unindent(),
 5866    );
 5867
 5868    cx.update_editor(|view, cx| {
 5869        view.handle_input("{", cx);
 5870        view.handle_input("{", cx);
 5871        view.move_right(&MoveRight, cx);
 5872        view.move_right(&MoveRight, cx);
 5873        view.move_left(&MoveLeft, cx);
 5874        view.move_left(&MoveLeft, cx);
 5875        view.backspace(&Default::default(), cx);
 5876    });
 5877
 5878    cx.assert_editor_state(
 5879        &"
 5880            {ˇ}
 5881            {ˇ}]]
 5882            {ˇ}
 5883        "
 5884        .unindent(),
 5885    );
 5886
 5887    cx.update_editor(|view, cx| {
 5888        view.backspace(&Default::default(), cx);
 5889    });
 5890
 5891    cx.assert_editor_state(
 5892        &"
 5893            ˇ
 5894            ˇ]]
 5895            ˇ
 5896        "
 5897        .unindent(),
 5898    );
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let language = Arc::new(Language::new(
 5906        LanguageConfig::default(),
 5907        Some(tree_sitter_rust::language()),
 5908    ));
 5909
 5910    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5911    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5912    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5913    editor
 5914        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5915        .await;
 5916
 5917    _ = editor.update(cx, |editor, cx| {
 5918        editor.set_auto_replace_emoji_shortcode(true);
 5919
 5920        editor.handle_input("Hello ", cx);
 5921        editor.handle_input(":wave", cx);
 5922        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5923
 5924        editor.handle_input(":", cx);
 5925        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5926
 5927        editor.handle_input(" :smile", cx);
 5928        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5929
 5930        editor.handle_input(":", cx);
 5931        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5932
 5933        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5934        editor.handle_input(":wave", cx);
 5935        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5936
 5937        editor.handle_input(":", cx);
 5938        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5939
 5940        editor.handle_input(":1", cx);
 5941        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5942
 5943        editor.handle_input(":", cx);
 5944        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5945
 5946        // Ensure shortcode does not get replaced when it is part of a word
 5947        editor.handle_input(" Test:wave", cx);
 5948        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5949
 5950        editor.handle_input(":", cx);
 5951        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5952
 5953        editor.set_auto_replace_emoji_shortcode(false);
 5954
 5955        // Ensure shortcode does not get replaced when auto replace is off
 5956        editor.handle_input(" :wave", cx);
 5957        assert_eq!(
 5958            editor.text(cx),
 5959            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5960        );
 5961
 5962        editor.handle_input(":", cx);
 5963        assert_eq!(
 5964            editor.text(cx),
 5965            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5966        );
 5967    });
 5968}
 5969
 5970#[gpui::test]
 5971async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5972    init_test(cx, |_| {});
 5973
 5974    let (text, insertion_ranges) = marked_text_ranges(
 5975        indoc! {"
 5976            a.ˇ b
 5977            a.ˇ b
 5978            a.ˇ b
 5979        "},
 5980        false,
 5981    );
 5982
 5983    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5984    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5985
 5986    _ = editor.update(cx, |editor, cx| {
 5987        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5988
 5989        editor
 5990            .insert_snippet(&insertion_ranges, snippet, cx)
 5991            .unwrap();
 5992
 5993        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5994            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5995            assert_eq!(editor.text(cx), expected_text);
 5996            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5997        }
 5998
 5999        assert(
 6000            editor,
 6001            cx,
 6002            indoc! {"
 6003                a.f(«one», two, «three») b
 6004                a.f(«one», two, «three») b
 6005                a.f(«one», two, «three») b
 6006            "},
 6007        );
 6008
 6009        // Can't move earlier than the first tab stop
 6010        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6011        assert(
 6012            editor,
 6013            cx,
 6014            indoc! {"
 6015                a.f(«one», two, «three») b
 6016                a.f(«one», two, «three») b
 6017                a.f(«one», two, «three») b
 6018            "},
 6019        );
 6020
 6021        assert!(editor.move_to_next_snippet_tabstop(cx));
 6022        assert(
 6023            editor,
 6024            cx,
 6025            indoc! {"
 6026                a.f(one, «two», three) b
 6027                a.f(one, «two», three) b
 6028                a.f(one, «two», three) b
 6029            "},
 6030        );
 6031
 6032        editor.move_to_prev_snippet_tabstop(cx);
 6033        assert(
 6034            editor,
 6035            cx,
 6036            indoc! {"
 6037                a.f(«one», two, «three») b
 6038                a.f(«one», two, «three») b
 6039                a.f(«one», two, «three») b
 6040            "},
 6041        );
 6042
 6043        assert!(editor.move_to_next_snippet_tabstop(cx));
 6044        assert(
 6045            editor,
 6046            cx,
 6047            indoc! {"
 6048                a.f(one, «two», three) b
 6049                a.f(one, «two», three) b
 6050                a.f(one, «two», three) b
 6051            "},
 6052        );
 6053        assert!(editor.move_to_next_snippet_tabstop(cx));
 6054        assert(
 6055            editor,
 6056            cx,
 6057            indoc! {"
 6058                a.f(one, two, three)ˇ b
 6059                a.f(one, two, three)ˇ b
 6060                a.f(one, two, three)ˇ b
 6061            "},
 6062        );
 6063
 6064        // As soon as the last tab stop is reached, snippet state is gone
 6065        editor.move_to_prev_snippet_tabstop(cx);
 6066        assert(
 6067            editor,
 6068            cx,
 6069            indoc! {"
 6070                a.f(one, two, three)ˇ b
 6071                a.f(one, two, three)ˇ b
 6072                a.f(one, two, three)ˇ b
 6073            "},
 6074        );
 6075    });
 6076}
 6077
 6078#[gpui::test]
 6079async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6080    init_test(cx, |_| {});
 6081
 6082    let fs = FakeFs::new(cx.executor());
 6083    fs.insert_file("/file.rs", Default::default()).await;
 6084
 6085    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6086
 6087    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6088    language_registry.add(rust_lang());
 6089    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6090        "Rust",
 6091        FakeLspAdapter {
 6092            capabilities: lsp::ServerCapabilities {
 6093                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6094                ..Default::default()
 6095            },
 6096            ..Default::default()
 6097        },
 6098    );
 6099
 6100    let buffer = project
 6101        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6102        .await
 6103        .unwrap();
 6104
 6105    cx.executor().start_waiting();
 6106    let fake_server = fake_servers.next().await.unwrap();
 6107
 6108    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6109    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6110    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6111    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6112
 6113    let save = editor
 6114        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6115        .unwrap();
 6116    fake_server
 6117        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6118            assert_eq!(
 6119                params.text_document.uri,
 6120                lsp::Url::from_file_path("/file.rs").unwrap()
 6121            );
 6122            assert_eq!(params.options.tab_size, 4);
 6123            Ok(Some(vec![lsp::TextEdit::new(
 6124                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6125                ", ".to_string(),
 6126            )]))
 6127        })
 6128        .next()
 6129        .await;
 6130    cx.executor().start_waiting();
 6131    save.await;
 6132
 6133    assert_eq!(
 6134        editor.update(cx, |editor, cx| editor.text(cx)),
 6135        "one, two\nthree\n"
 6136    );
 6137    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6138
 6139    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6140    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6141
 6142    // Ensure we can still save even if formatting hangs.
 6143    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6144        assert_eq!(
 6145            params.text_document.uri,
 6146            lsp::Url::from_file_path("/file.rs").unwrap()
 6147        );
 6148        futures::future::pending::<()>().await;
 6149        unreachable!()
 6150    });
 6151    let save = editor
 6152        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6153        .unwrap();
 6154    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6155    cx.executor().start_waiting();
 6156    save.await;
 6157    assert_eq!(
 6158        editor.update(cx, |editor, cx| editor.text(cx)),
 6159        "one\ntwo\nthree\n"
 6160    );
 6161    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6162
 6163    // For non-dirty buffer, no formatting request should be sent
 6164    let save = editor
 6165        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6166        .unwrap();
 6167    let _pending_format_request = fake_server
 6168        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6169            panic!("Should not be invoked on non-dirty buffer");
 6170        })
 6171        .next();
 6172    cx.executor().start_waiting();
 6173    save.await;
 6174
 6175    // Set rust language override and assert overridden tabsize is sent to language server
 6176    update_test_language_settings(cx, |settings| {
 6177        settings.languages.insert(
 6178            "Rust".into(),
 6179            LanguageSettingsContent {
 6180                tab_size: NonZeroU32::new(8),
 6181                ..Default::default()
 6182            },
 6183        );
 6184    });
 6185
 6186    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6187    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6188    let save = editor
 6189        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6190        .unwrap();
 6191    fake_server
 6192        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6193            assert_eq!(
 6194                params.text_document.uri,
 6195                lsp::Url::from_file_path("/file.rs").unwrap()
 6196            );
 6197            assert_eq!(params.options.tab_size, 8);
 6198            Ok(Some(vec![]))
 6199        })
 6200        .next()
 6201        .await;
 6202    cx.executor().start_waiting();
 6203    save.await;
 6204}
 6205
 6206#[gpui::test]
 6207async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6208    init_test(cx, |_| {});
 6209
 6210    let cols = 4;
 6211    let rows = 10;
 6212    let sample_text_1 = sample_text(rows, cols, 'a');
 6213    assert_eq!(
 6214        sample_text_1,
 6215        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6216    );
 6217    let sample_text_2 = sample_text(rows, cols, 'l');
 6218    assert_eq!(
 6219        sample_text_2,
 6220        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6221    );
 6222    let sample_text_3 = sample_text(rows, cols, 'v');
 6223    assert_eq!(
 6224        sample_text_3,
 6225        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6226    );
 6227
 6228    let fs = FakeFs::new(cx.executor());
 6229    fs.insert_tree(
 6230        "/a",
 6231        json!({
 6232            "main.rs": sample_text_1,
 6233            "other.rs": sample_text_2,
 6234            "lib.rs": sample_text_3,
 6235        }),
 6236    )
 6237    .await;
 6238
 6239    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6240    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6241    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6242
 6243    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6244    language_registry.add(rust_lang());
 6245    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6246        "Rust",
 6247        FakeLspAdapter {
 6248            capabilities: lsp::ServerCapabilities {
 6249                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6250                ..Default::default()
 6251            },
 6252            ..Default::default()
 6253        },
 6254    );
 6255
 6256    let worktree = project.update(cx, |project, _| {
 6257        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 6258        assert_eq!(worktrees.len(), 1);
 6259        worktrees.pop().unwrap()
 6260    });
 6261    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6262
 6263    let buffer_1 = project
 6264        .update(cx, |project, cx| {
 6265            project.open_buffer((worktree_id, "main.rs"), cx)
 6266        })
 6267        .await
 6268        .unwrap();
 6269    let buffer_2 = project
 6270        .update(cx, |project, cx| {
 6271            project.open_buffer((worktree_id, "other.rs"), cx)
 6272        })
 6273        .await
 6274        .unwrap();
 6275    let buffer_3 = project
 6276        .update(cx, |project, cx| {
 6277            project.open_buffer((worktree_id, "lib.rs"), cx)
 6278        })
 6279        .await
 6280        .unwrap();
 6281
 6282    let multi_buffer = cx.new_model(|cx| {
 6283        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6284        multi_buffer.push_excerpts(
 6285            buffer_1.clone(),
 6286            [
 6287                ExcerptRange {
 6288                    context: Point::new(0, 0)..Point::new(3, 0),
 6289                    primary: None,
 6290                },
 6291                ExcerptRange {
 6292                    context: Point::new(5, 0)..Point::new(7, 0),
 6293                    primary: None,
 6294                },
 6295                ExcerptRange {
 6296                    context: Point::new(9, 0)..Point::new(10, 4),
 6297                    primary: None,
 6298                },
 6299            ],
 6300            cx,
 6301        );
 6302        multi_buffer.push_excerpts(
 6303            buffer_2.clone(),
 6304            [
 6305                ExcerptRange {
 6306                    context: Point::new(0, 0)..Point::new(3, 0),
 6307                    primary: None,
 6308                },
 6309                ExcerptRange {
 6310                    context: Point::new(5, 0)..Point::new(7, 0),
 6311                    primary: None,
 6312                },
 6313                ExcerptRange {
 6314                    context: Point::new(9, 0)..Point::new(10, 4),
 6315                    primary: None,
 6316                },
 6317            ],
 6318            cx,
 6319        );
 6320        multi_buffer.push_excerpts(
 6321            buffer_3.clone(),
 6322            [
 6323                ExcerptRange {
 6324                    context: Point::new(0, 0)..Point::new(3, 0),
 6325                    primary: None,
 6326                },
 6327                ExcerptRange {
 6328                    context: Point::new(5, 0)..Point::new(7, 0),
 6329                    primary: None,
 6330                },
 6331                ExcerptRange {
 6332                    context: Point::new(9, 0)..Point::new(10, 4),
 6333                    primary: None,
 6334                },
 6335            ],
 6336            cx,
 6337        );
 6338        multi_buffer
 6339    });
 6340    let multi_buffer_editor = cx.new_view(|cx| {
 6341        Editor::new(
 6342            EditorMode::Full,
 6343            multi_buffer,
 6344            Some(project.clone()),
 6345            true,
 6346            cx,
 6347        )
 6348    });
 6349
 6350    multi_buffer_editor.update(cx, |editor, cx| {
 6351        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6352        editor.insert("|one|two|three|", cx);
 6353    });
 6354    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6355    multi_buffer_editor.update(cx, |editor, cx| {
 6356        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6357            s.select_ranges(Some(60..70))
 6358        });
 6359        editor.insert("|four|five|six|", cx);
 6360    });
 6361    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6362
 6363    // First two buffers should be edited, but not the third one.
 6364    assert_eq!(
 6365        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6366        "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}",
 6367    );
 6368    buffer_1.update(cx, |buffer, _| {
 6369        assert!(buffer.is_dirty());
 6370        assert_eq!(
 6371            buffer.text(),
 6372            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6373        )
 6374    });
 6375    buffer_2.update(cx, |buffer, _| {
 6376        assert!(buffer.is_dirty());
 6377        assert_eq!(
 6378            buffer.text(),
 6379            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6380        )
 6381    });
 6382    buffer_3.update(cx, |buffer, _| {
 6383        assert!(!buffer.is_dirty());
 6384        assert_eq!(buffer.text(), sample_text_3,)
 6385    });
 6386
 6387    cx.executor().start_waiting();
 6388    let save = multi_buffer_editor
 6389        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6390        .unwrap();
 6391
 6392    let fake_server = fake_servers.next().await.unwrap();
 6393    fake_server
 6394        .server
 6395        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6396            Ok(Some(vec![lsp::TextEdit::new(
 6397                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6398                format!("[{} formatted]", params.text_document.uri),
 6399            )]))
 6400        })
 6401        .detach();
 6402    save.await;
 6403
 6404    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6405    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6406    assert_eq!(
 6407        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6408        "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}",
 6409    );
 6410    buffer_1.update(cx, |buffer, _| {
 6411        assert!(!buffer.is_dirty());
 6412        assert_eq!(
 6413            buffer.text(),
 6414            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6415        )
 6416    });
 6417    buffer_2.update(cx, |buffer, _| {
 6418        assert!(!buffer.is_dirty());
 6419        assert_eq!(
 6420            buffer.text(),
 6421            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6422        )
 6423    });
 6424    buffer_3.update(cx, |buffer, _| {
 6425        assert!(!buffer.is_dirty());
 6426        assert_eq!(buffer.text(), sample_text_3,)
 6427    });
 6428}
 6429
 6430#[gpui::test]
 6431async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6432    init_test(cx, |_| {});
 6433
 6434    let fs = FakeFs::new(cx.executor());
 6435    fs.insert_file("/file.rs", Default::default()).await;
 6436
 6437    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6438
 6439    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6440    language_registry.add(rust_lang());
 6441    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6442        "Rust",
 6443        FakeLspAdapter {
 6444            capabilities: lsp::ServerCapabilities {
 6445                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6446                ..Default::default()
 6447            },
 6448            ..Default::default()
 6449        },
 6450    );
 6451
 6452    let buffer = project
 6453        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6454        .await
 6455        .unwrap();
 6456
 6457    cx.executor().start_waiting();
 6458    let fake_server = fake_servers.next().await.unwrap();
 6459
 6460    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6461    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6462    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6463    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6464
 6465    let save = editor
 6466        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6467        .unwrap();
 6468    fake_server
 6469        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6470            assert_eq!(
 6471                params.text_document.uri,
 6472                lsp::Url::from_file_path("/file.rs").unwrap()
 6473            );
 6474            assert_eq!(params.options.tab_size, 4);
 6475            Ok(Some(vec![lsp::TextEdit::new(
 6476                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6477                ", ".to_string(),
 6478            )]))
 6479        })
 6480        .next()
 6481        .await;
 6482    cx.executor().start_waiting();
 6483    save.await;
 6484    assert_eq!(
 6485        editor.update(cx, |editor, cx| editor.text(cx)),
 6486        "one, two\nthree\n"
 6487    );
 6488    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6489
 6490    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6491    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6492
 6493    // Ensure we can still save even if formatting hangs.
 6494    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6495        move |params, _| async move {
 6496            assert_eq!(
 6497                params.text_document.uri,
 6498                lsp::Url::from_file_path("/file.rs").unwrap()
 6499            );
 6500            futures::future::pending::<()>().await;
 6501            unreachable!()
 6502        },
 6503    );
 6504    let save = editor
 6505        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6506        .unwrap();
 6507    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6508    cx.executor().start_waiting();
 6509    save.await;
 6510    assert_eq!(
 6511        editor.update(cx, |editor, cx| editor.text(cx)),
 6512        "one\ntwo\nthree\n"
 6513    );
 6514    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6515
 6516    // For non-dirty buffer, no formatting request should be sent
 6517    let save = editor
 6518        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6519        .unwrap();
 6520    let _pending_format_request = fake_server
 6521        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6522            panic!("Should not be invoked on non-dirty buffer");
 6523        })
 6524        .next();
 6525    cx.executor().start_waiting();
 6526    save.await;
 6527
 6528    // Set Rust language override and assert overridden tabsize is sent to language server
 6529    update_test_language_settings(cx, |settings| {
 6530        settings.languages.insert(
 6531            "Rust".into(),
 6532            LanguageSettingsContent {
 6533                tab_size: NonZeroU32::new(8),
 6534                ..Default::default()
 6535            },
 6536        );
 6537    });
 6538
 6539    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6540    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6541    let save = editor
 6542        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6543        .unwrap();
 6544    fake_server
 6545        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6546            assert_eq!(
 6547                params.text_document.uri,
 6548                lsp::Url::from_file_path("/file.rs").unwrap()
 6549            );
 6550            assert_eq!(params.options.tab_size, 8);
 6551            Ok(Some(vec![]))
 6552        })
 6553        .next()
 6554        .await;
 6555    cx.executor().start_waiting();
 6556    save.await;
 6557}
 6558
 6559#[gpui::test]
 6560async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6561    init_test(cx, |settings| {
 6562        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6563    });
 6564
 6565    let fs = FakeFs::new(cx.executor());
 6566    fs.insert_file("/file.rs", Default::default()).await;
 6567
 6568    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6569
 6570    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6571    language_registry.add(Arc::new(Language::new(
 6572        LanguageConfig {
 6573            name: "Rust".into(),
 6574            matcher: LanguageMatcher {
 6575                path_suffixes: vec!["rs".to_string()],
 6576                ..Default::default()
 6577            },
 6578            ..LanguageConfig::default()
 6579        },
 6580        Some(tree_sitter_rust::language()),
 6581    )));
 6582    update_test_language_settings(cx, |settings| {
 6583        // Enable Prettier formatting for the same buffer, and ensure
 6584        // LSP is called instead of Prettier.
 6585        settings.defaults.prettier = Some(PrettierSettings {
 6586            allowed: true,
 6587            ..PrettierSettings::default()
 6588        });
 6589    });
 6590    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6591        "Rust",
 6592        FakeLspAdapter {
 6593            capabilities: lsp::ServerCapabilities {
 6594                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6595                ..Default::default()
 6596            },
 6597            ..Default::default()
 6598        },
 6599    );
 6600
 6601    let buffer = project
 6602        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6603        .await
 6604        .unwrap();
 6605
 6606    cx.executor().start_waiting();
 6607    let fake_server = fake_servers.next().await.unwrap();
 6608
 6609    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6610    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6611    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6612
 6613    let format = editor
 6614        .update(cx, |editor, cx| {
 6615            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6616        })
 6617        .unwrap();
 6618    fake_server
 6619        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6620            assert_eq!(
 6621                params.text_document.uri,
 6622                lsp::Url::from_file_path("/file.rs").unwrap()
 6623            );
 6624            assert_eq!(params.options.tab_size, 4);
 6625            Ok(Some(vec![lsp::TextEdit::new(
 6626                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6627                ", ".to_string(),
 6628            )]))
 6629        })
 6630        .next()
 6631        .await;
 6632    cx.executor().start_waiting();
 6633    format.await;
 6634    assert_eq!(
 6635        editor.update(cx, |editor, cx| editor.text(cx)),
 6636        "one, two\nthree\n"
 6637    );
 6638
 6639    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6640    // Ensure we don't lock if formatting hangs.
 6641    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6642        assert_eq!(
 6643            params.text_document.uri,
 6644            lsp::Url::from_file_path("/file.rs").unwrap()
 6645        );
 6646        futures::future::pending::<()>().await;
 6647        unreachable!()
 6648    });
 6649    let format = editor
 6650        .update(cx, |editor, cx| {
 6651            editor.perform_format(project, FormatTrigger::Manual, cx)
 6652        })
 6653        .unwrap();
 6654    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6655    cx.executor().start_waiting();
 6656    format.await;
 6657    assert_eq!(
 6658        editor.update(cx, |editor, cx| editor.text(cx)),
 6659        "one\ntwo\nthree\n"
 6660    );
 6661}
 6662
 6663#[gpui::test]
 6664async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6665    init_test(cx, |_| {});
 6666
 6667    let mut cx = EditorLspTestContext::new_rust(
 6668        lsp::ServerCapabilities {
 6669            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6670            ..Default::default()
 6671        },
 6672        cx,
 6673    )
 6674    .await;
 6675
 6676    cx.set_state(indoc! {"
 6677        one.twoˇ
 6678    "});
 6679
 6680    // The format request takes a long time. When it completes, it inserts
 6681    // a newline and an indent before the `.`
 6682    cx.lsp
 6683        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6684            let executor = cx.background_executor().clone();
 6685            async move {
 6686                executor.timer(Duration::from_millis(100)).await;
 6687                Ok(Some(vec![lsp::TextEdit {
 6688                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6689                    new_text: "\n    ".into(),
 6690                }]))
 6691            }
 6692        });
 6693
 6694    // Submit a format request.
 6695    let format_1 = cx
 6696        .update_editor(|editor, cx| editor.format(&Format, cx))
 6697        .unwrap();
 6698    cx.executor().run_until_parked();
 6699
 6700    // Submit a second format request.
 6701    let format_2 = cx
 6702        .update_editor(|editor, cx| editor.format(&Format, cx))
 6703        .unwrap();
 6704    cx.executor().run_until_parked();
 6705
 6706    // Wait for both format requests to complete
 6707    cx.executor().advance_clock(Duration::from_millis(200));
 6708    cx.executor().start_waiting();
 6709    format_1.await.unwrap();
 6710    cx.executor().start_waiting();
 6711    format_2.await.unwrap();
 6712
 6713    // The formatting edits only happens once.
 6714    cx.assert_editor_state(indoc! {"
 6715        one
 6716            .twoˇ
 6717    "});
 6718}
 6719
 6720#[gpui::test]
 6721async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6722    init_test(cx, |settings| {
 6723        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6724    });
 6725
 6726    let mut cx = EditorLspTestContext::new_rust(
 6727        lsp::ServerCapabilities {
 6728            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6729            ..Default::default()
 6730        },
 6731        cx,
 6732    )
 6733    .await;
 6734
 6735    // Set up a buffer white some trailing whitespace and no trailing newline.
 6736    cx.set_state(
 6737        &[
 6738            "one ",   //
 6739            "twoˇ",   //
 6740            "three ", //
 6741            "four",   //
 6742        ]
 6743        .join("\n"),
 6744    );
 6745
 6746    // Submit a format request.
 6747    let format = cx
 6748        .update_editor(|editor, cx| editor.format(&Format, cx))
 6749        .unwrap();
 6750
 6751    // Record which buffer changes have been sent to the language server
 6752    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6753    cx.lsp
 6754        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6755            let buffer_changes = buffer_changes.clone();
 6756            move |params, _| {
 6757                buffer_changes.lock().extend(
 6758                    params
 6759                        .content_changes
 6760                        .into_iter()
 6761                        .map(|e| (e.range.unwrap(), e.text)),
 6762                );
 6763            }
 6764        });
 6765
 6766    // Handle formatting requests to the language server.
 6767    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6768        let buffer_changes = buffer_changes.clone();
 6769        move |_, _| {
 6770            // When formatting is requested, trailing whitespace has already been stripped,
 6771            // and the trailing newline has already been added.
 6772            assert_eq!(
 6773                &buffer_changes.lock()[1..],
 6774                &[
 6775                    (
 6776                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6777                        "".into()
 6778                    ),
 6779                    (
 6780                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6781                        "".into()
 6782                    ),
 6783                    (
 6784                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6785                        "\n".into()
 6786                    ),
 6787                ]
 6788            );
 6789
 6790            // Insert blank lines between each line of the buffer.
 6791            async move {
 6792                Ok(Some(vec![
 6793                    lsp::TextEdit {
 6794                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6795                        new_text: "\n".into(),
 6796                    },
 6797                    lsp::TextEdit {
 6798                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6799                        new_text: "\n".into(),
 6800                    },
 6801                ]))
 6802            }
 6803        }
 6804    });
 6805
 6806    // After formatting the buffer, the trailing whitespace is stripped,
 6807    // a newline is appended, and the edits provided by the language server
 6808    // have been applied.
 6809    format.await.unwrap();
 6810    cx.assert_editor_state(
 6811        &[
 6812            "one",   //
 6813            "",      //
 6814            "twoˇ",  //
 6815            "",      //
 6816            "three", //
 6817            "four",  //
 6818            "",      //
 6819        ]
 6820        .join("\n"),
 6821    );
 6822
 6823    // Undoing the formatting undoes the trailing whitespace removal, the
 6824    // trailing newline, and the LSP edits.
 6825    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6826    cx.assert_editor_state(
 6827        &[
 6828            "one ",   //
 6829            "twoˇ",   //
 6830            "three ", //
 6831            "four",   //
 6832        ]
 6833        .join("\n"),
 6834    );
 6835}
 6836
 6837#[gpui::test]
 6838async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6839    cx: &mut gpui::TestAppContext,
 6840) {
 6841    init_test(cx, |_| {});
 6842
 6843    cx.update(|cx| {
 6844        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6845            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6846                settings.auto_signature_help = Some(true);
 6847            });
 6848        });
 6849    });
 6850
 6851    let mut cx = EditorLspTestContext::new_rust(
 6852        lsp::ServerCapabilities {
 6853            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6854                ..Default::default()
 6855            }),
 6856            ..Default::default()
 6857        },
 6858        cx,
 6859    )
 6860    .await;
 6861
 6862    let language = Language::new(
 6863        LanguageConfig {
 6864            name: "Rust".into(),
 6865            brackets: BracketPairConfig {
 6866                pairs: vec![
 6867                    BracketPair {
 6868                        start: "{".to_string(),
 6869                        end: "}".to_string(),
 6870                        close: true,
 6871                        surround: true,
 6872                        newline: true,
 6873                    },
 6874                    BracketPair {
 6875                        start: "(".to_string(),
 6876                        end: ")".to_string(),
 6877                        close: true,
 6878                        surround: true,
 6879                        newline: true,
 6880                    },
 6881                    BracketPair {
 6882                        start: "/*".to_string(),
 6883                        end: " */".to_string(),
 6884                        close: true,
 6885                        surround: true,
 6886                        newline: true,
 6887                    },
 6888                    BracketPair {
 6889                        start: "[".to_string(),
 6890                        end: "]".to_string(),
 6891                        close: false,
 6892                        surround: false,
 6893                        newline: true,
 6894                    },
 6895                    BracketPair {
 6896                        start: "\"".to_string(),
 6897                        end: "\"".to_string(),
 6898                        close: true,
 6899                        surround: true,
 6900                        newline: false,
 6901                    },
 6902                    BracketPair {
 6903                        start: "<".to_string(),
 6904                        end: ">".to_string(),
 6905                        close: false,
 6906                        surround: true,
 6907                        newline: true,
 6908                    },
 6909                ],
 6910                ..Default::default()
 6911            },
 6912            autoclose_before: "})]".to_string(),
 6913            ..Default::default()
 6914        },
 6915        Some(tree_sitter_rust::language()),
 6916    );
 6917    let language = Arc::new(language);
 6918
 6919    cx.language_registry().add(language.clone());
 6920    cx.update_buffer(|buffer, cx| {
 6921        buffer.set_language(Some(language), cx);
 6922    });
 6923
 6924    cx.set_state(
 6925        &r#"
 6926            fn main() {
 6927                sampleˇ
 6928            }
 6929        "#
 6930        .unindent(),
 6931    );
 6932
 6933    cx.update_editor(|view, cx| {
 6934        view.handle_input("(", cx);
 6935    });
 6936    cx.assert_editor_state(
 6937        &"
 6938            fn main() {
 6939                sample(ˇ)
 6940            }
 6941        "
 6942        .unindent(),
 6943    );
 6944
 6945    let mocked_response = lsp::SignatureHelp {
 6946        signatures: vec![lsp::SignatureInformation {
 6947            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6948            documentation: None,
 6949            parameters: Some(vec![
 6950                lsp::ParameterInformation {
 6951                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6952                    documentation: None,
 6953                },
 6954                lsp::ParameterInformation {
 6955                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6956                    documentation: None,
 6957                },
 6958            ]),
 6959            active_parameter: None,
 6960        }],
 6961        active_signature: Some(0),
 6962        active_parameter: Some(0),
 6963    };
 6964    handle_signature_help_request(&mut cx, mocked_response).await;
 6965
 6966    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 6967        .await;
 6968
 6969    cx.editor(|editor, _| {
 6970        let signature_help_state = editor.signature_help_state.popover().cloned();
 6971        assert!(signature_help_state.is_some());
 6972        let ParsedMarkdown {
 6973            text, highlights, ..
 6974        } = signature_help_state.unwrap().parsed_content;
 6975        assert_eq!(text, "param1: u8, param2: u8");
 6976        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 6977    });
 6978}
 6979
 6980#[gpui::test]
 6981async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 6982    init_test(cx, |_| {});
 6983
 6984    cx.update(|cx| {
 6985        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6986            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6987                settings.auto_signature_help = Some(false);
 6988                settings.show_signature_help_after_edits = Some(false);
 6989            });
 6990        });
 6991    });
 6992
 6993    let mut cx = EditorLspTestContext::new_rust(
 6994        lsp::ServerCapabilities {
 6995            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6996                ..Default::default()
 6997            }),
 6998            ..Default::default()
 6999        },
 7000        cx,
 7001    )
 7002    .await;
 7003
 7004    let language = Language::new(
 7005        LanguageConfig {
 7006            name: "Rust".into(),
 7007            brackets: BracketPairConfig {
 7008                pairs: vec![
 7009                    BracketPair {
 7010                        start: "{".to_string(),
 7011                        end: "}".to_string(),
 7012                        close: true,
 7013                        surround: true,
 7014                        newline: true,
 7015                    },
 7016                    BracketPair {
 7017                        start: "(".to_string(),
 7018                        end: ")".to_string(),
 7019                        close: true,
 7020                        surround: true,
 7021                        newline: true,
 7022                    },
 7023                    BracketPair {
 7024                        start: "/*".to_string(),
 7025                        end: " */".to_string(),
 7026                        close: true,
 7027                        surround: true,
 7028                        newline: true,
 7029                    },
 7030                    BracketPair {
 7031                        start: "[".to_string(),
 7032                        end: "]".to_string(),
 7033                        close: false,
 7034                        surround: false,
 7035                        newline: true,
 7036                    },
 7037                    BracketPair {
 7038                        start: "\"".to_string(),
 7039                        end: "\"".to_string(),
 7040                        close: true,
 7041                        surround: true,
 7042                        newline: false,
 7043                    },
 7044                    BracketPair {
 7045                        start: "<".to_string(),
 7046                        end: ">".to_string(),
 7047                        close: false,
 7048                        surround: true,
 7049                        newline: true,
 7050                    },
 7051                ],
 7052                ..Default::default()
 7053            },
 7054            autoclose_before: "})]".to_string(),
 7055            ..Default::default()
 7056        },
 7057        Some(tree_sitter_rust::language()),
 7058    );
 7059    let language = Arc::new(language);
 7060
 7061    cx.language_registry().add(language.clone());
 7062    cx.update_buffer(|buffer, cx| {
 7063        buffer.set_language(Some(language), cx);
 7064    });
 7065
 7066    // Ensure that signature_help is not called when no signature help is enabled.
 7067    cx.set_state(
 7068        &r#"
 7069            fn main() {
 7070                sampleˇ
 7071            }
 7072        "#
 7073        .unindent(),
 7074    );
 7075    cx.update_editor(|view, cx| {
 7076        view.handle_input("(", cx);
 7077    });
 7078    cx.assert_editor_state(
 7079        &"
 7080            fn main() {
 7081                sample(ˇ)
 7082            }
 7083        "
 7084        .unindent(),
 7085    );
 7086    cx.editor(|editor, _| {
 7087        assert!(editor.signature_help_state.task().is_none());
 7088    });
 7089
 7090    let mocked_response = lsp::SignatureHelp {
 7091        signatures: vec![lsp::SignatureInformation {
 7092            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7093            documentation: None,
 7094            parameters: Some(vec![
 7095                lsp::ParameterInformation {
 7096                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7097                    documentation: None,
 7098                },
 7099                lsp::ParameterInformation {
 7100                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7101                    documentation: None,
 7102                },
 7103            ]),
 7104            active_parameter: None,
 7105        }],
 7106        active_signature: Some(0),
 7107        active_parameter: Some(0),
 7108    };
 7109
 7110    // Ensure that signature_help is called when enabled afte edits
 7111    cx.update(|cx| {
 7112        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7113            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7114                settings.auto_signature_help = Some(false);
 7115                settings.show_signature_help_after_edits = Some(true);
 7116            });
 7117        });
 7118    });
 7119    cx.set_state(
 7120        &r#"
 7121            fn main() {
 7122                sampleˇ
 7123            }
 7124        "#
 7125        .unindent(),
 7126    );
 7127    cx.update_editor(|view, cx| {
 7128        view.handle_input("(", cx);
 7129    });
 7130    cx.assert_editor_state(
 7131        &"
 7132            fn main() {
 7133                sample(ˇ)
 7134            }
 7135        "
 7136        .unindent(),
 7137    );
 7138    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7139    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7140        .await;
 7141    cx.update_editor(|editor, _| {
 7142        let signature_help_state = editor.signature_help_state.popover().cloned();
 7143        assert!(signature_help_state.is_some());
 7144        let ParsedMarkdown {
 7145            text, highlights, ..
 7146        } = signature_help_state.unwrap().parsed_content;
 7147        assert_eq!(text, "param1: u8, param2: u8");
 7148        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7149        editor.signature_help_state = SignatureHelpState::default();
 7150    });
 7151
 7152    // Ensure that signature_help is called when auto signature help override is enabled
 7153    cx.update(|cx| {
 7154        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7155            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7156                settings.auto_signature_help = Some(true);
 7157                settings.show_signature_help_after_edits = Some(false);
 7158            });
 7159        });
 7160    });
 7161    cx.set_state(
 7162        &r#"
 7163            fn main() {
 7164                sampleˇ
 7165            }
 7166        "#
 7167        .unindent(),
 7168    );
 7169    cx.update_editor(|view, cx| {
 7170        view.handle_input("(", cx);
 7171    });
 7172    cx.assert_editor_state(
 7173        &"
 7174            fn main() {
 7175                sample(ˇ)
 7176            }
 7177        "
 7178        .unindent(),
 7179    );
 7180    handle_signature_help_request(&mut cx, mocked_response).await;
 7181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7182        .await;
 7183    cx.editor(|editor, _| {
 7184        let signature_help_state = editor.signature_help_state.popover().cloned();
 7185        assert!(signature_help_state.is_some());
 7186        let ParsedMarkdown {
 7187            text, highlights, ..
 7188        } = signature_help_state.unwrap().parsed_content;
 7189        assert_eq!(text, "param1: u8, param2: u8");
 7190        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7191    });
 7192}
 7193
 7194#[gpui::test]
 7195async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7196    init_test(cx, |_| {});
 7197    cx.update(|cx| {
 7198        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7199            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7200                settings.auto_signature_help = Some(true);
 7201            });
 7202        });
 7203    });
 7204
 7205    let mut cx = EditorLspTestContext::new_rust(
 7206        lsp::ServerCapabilities {
 7207            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7208                ..Default::default()
 7209            }),
 7210            ..Default::default()
 7211        },
 7212        cx,
 7213    )
 7214    .await;
 7215
 7216    // A test that directly calls `show_signature_help`
 7217    cx.update_editor(|editor, cx| {
 7218        editor.show_signature_help(&ShowSignatureHelp, cx);
 7219    });
 7220
 7221    let mocked_response = lsp::SignatureHelp {
 7222        signatures: vec![lsp::SignatureInformation {
 7223            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7224            documentation: None,
 7225            parameters: Some(vec![
 7226                lsp::ParameterInformation {
 7227                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7228                    documentation: None,
 7229                },
 7230                lsp::ParameterInformation {
 7231                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7232                    documentation: None,
 7233                },
 7234            ]),
 7235            active_parameter: None,
 7236        }],
 7237        active_signature: Some(0),
 7238        active_parameter: Some(0),
 7239    };
 7240    handle_signature_help_request(&mut cx, mocked_response).await;
 7241
 7242    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7243        .await;
 7244
 7245    cx.editor(|editor, _| {
 7246        let signature_help_state = editor.signature_help_state.popover().cloned();
 7247        assert!(signature_help_state.is_some());
 7248        let ParsedMarkdown {
 7249            text, highlights, ..
 7250        } = signature_help_state.unwrap().parsed_content;
 7251        assert_eq!(text, "param1: u8, param2: u8");
 7252        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7253    });
 7254
 7255    // When exiting outside from inside the brackets, `signature_help` is closed.
 7256    cx.set_state(indoc! {"
 7257        fn main() {
 7258            sample(ˇ);
 7259        }
 7260
 7261        fn sample(param1: u8, param2: u8) {}
 7262    "});
 7263
 7264    cx.update_editor(|editor, cx| {
 7265        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7266    });
 7267
 7268    let mocked_response = lsp::SignatureHelp {
 7269        signatures: Vec::new(),
 7270        active_signature: None,
 7271        active_parameter: None,
 7272    };
 7273    handle_signature_help_request(&mut cx, mocked_response).await;
 7274
 7275    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7276        .await;
 7277
 7278    cx.editor(|editor, _| {
 7279        assert!(!editor.signature_help_state.is_shown());
 7280    });
 7281
 7282    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7283    cx.set_state(indoc! {"
 7284        fn main() {
 7285            sample(ˇ);
 7286        }
 7287
 7288        fn sample(param1: u8, param2: u8) {}
 7289    "});
 7290
 7291    let mocked_response = lsp::SignatureHelp {
 7292        signatures: vec![lsp::SignatureInformation {
 7293            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7294            documentation: None,
 7295            parameters: Some(vec![
 7296                lsp::ParameterInformation {
 7297                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7298                    documentation: None,
 7299                },
 7300                lsp::ParameterInformation {
 7301                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7302                    documentation: None,
 7303                },
 7304            ]),
 7305            active_parameter: None,
 7306        }],
 7307        active_signature: Some(0),
 7308        active_parameter: Some(0),
 7309    };
 7310    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7311    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7312        .await;
 7313    cx.editor(|editor, _| {
 7314        assert!(editor.signature_help_state.is_shown());
 7315    });
 7316
 7317    // Restore the popover with more parameter input
 7318    cx.set_state(indoc! {"
 7319        fn main() {
 7320            sample(param1, param2ˇ);
 7321        }
 7322
 7323        fn sample(param1: u8, param2: u8) {}
 7324    "});
 7325
 7326    let mocked_response = lsp::SignatureHelp {
 7327        signatures: vec![lsp::SignatureInformation {
 7328            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7329            documentation: None,
 7330            parameters: Some(vec![
 7331                lsp::ParameterInformation {
 7332                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7333                    documentation: None,
 7334                },
 7335                lsp::ParameterInformation {
 7336                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7337                    documentation: None,
 7338                },
 7339            ]),
 7340            active_parameter: None,
 7341        }],
 7342        active_signature: Some(0),
 7343        active_parameter: Some(1),
 7344    };
 7345    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7346    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7347        .await;
 7348
 7349    // When selecting a range, the popover is gone.
 7350    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7351    cx.update_editor(|editor, cx| {
 7352        editor.change_selections(None, cx, |s| {
 7353            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7354        })
 7355    });
 7356    cx.assert_editor_state(indoc! {"
 7357        fn main() {
 7358            sample(param1, «ˇparam2»);
 7359        }
 7360
 7361        fn sample(param1: u8, param2: u8) {}
 7362    "});
 7363    cx.editor(|editor, _| {
 7364        assert!(!editor.signature_help_state.is_shown());
 7365    });
 7366
 7367    // When unselecting again, the popover is back if within the brackets.
 7368    cx.update_editor(|editor, cx| {
 7369        editor.change_selections(None, cx, |s| {
 7370            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7371        })
 7372    });
 7373    cx.assert_editor_state(indoc! {"
 7374        fn main() {
 7375            sample(param1, ˇparam2);
 7376        }
 7377
 7378        fn sample(param1: u8, param2: u8) {}
 7379    "});
 7380    handle_signature_help_request(&mut cx, mocked_response).await;
 7381    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7382        .await;
 7383    cx.editor(|editor, _| {
 7384        assert!(editor.signature_help_state.is_shown());
 7385    });
 7386
 7387    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7388    cx.update_editor(|editor, cx| {
 7389        editor.change_selections(None, cx, |s| {
 7390            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7391            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7392        })
 7393    });
 7394    cx.assert_editor_state(indoc! {"
 7395        fn main() {
 7396            sample(param1, ˇparam2);
 7397        }
 7398
 7399        fn sample(param1: u8, param2: u8) {}
 7400    "});
 7401
 7402    let mocked_response = lsp::SignatureHelp {
 7403        signatures: vec![lsp::SignatureInformation {
 7404            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7405            documentation: None,
 7406            parameters: Some(vec![
 7407                lsp::ParameterInformation {
 7408                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7409                    documentation: None,
 7410                },
 7411                lsp::ParameterInformation {
 7412                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7413                    documentation: None,
 7414                },
 7415            ]),
 7416            active_parameter: None,
 7417        }],
 7418        active_signature: Some(0),
 7419        active_parameter: Some(1),
 7420    };
 7421    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7422    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7423        .await;
 7424    cx.update_editor(|editor, cx| {
 7425        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7426    });
 7427    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7428        .await;
 7429    cx.update_editor(|editor, cx| {
 7430        editor.change_selections(None, cx, |s| {
 7431            s.select_ranges(Some(Point::new(1, 25)..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    cx.update_editor(|editor, cx| {
 7442        editor.change_selections(None, cx, |s| {
 7443            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7444        })
 7445    });
 7446    cx.assert_editor_state(indoc! {"
 7447        fn main() {
 7448            sample(param1, ˇparam2);
 7449        }
 7450
 7451        fn sample(param1: u8, param2: u8) {}
 7452    "});
 7453    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7454        .await;
 7455}
 7456
 7457#[gpui::test]
 7458async fn test_completion(cx: &mut gpui::TestAppContext) {
 7459    init_test(cx, |_| {});
 7460
 7461    let mut cx = EditorLspTestContext::new_rust(
 7462        lsp::ServerCapabilities {
 7463            completion_provider: Some(lsp::CompletionOptions {
 7464                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7465                resolve_provider: Some(true),
 7466                ..Default::default()
 7467            }),
 7468            ..Default::default()
 7469        },
 7470        cx,
 7471    )
 7472    .await;
 7473    let counter = Arc::new(AtomicUsize::new(0));
 7474
 7475    cx.set_state(indoc! {"
 7476        oneˇ
 7477        two
 7478        three
 7479    "});
 7480    cx.simulate_keystroke(".");
 7481    handle_completion_request(
 7482        &mut cx,
 7483        indoc! {"
 7484            one.|<>
 7485            two
 7486            three
 7487        "},
 7488        vec!["first_completion", "second_completion"],
 7489        counter.clone(),
 7490    )
 7491    .await;
 7492    cx.condition(|editor, _| editor.context_menu_visible())
 7493        .await;
 7494    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7495
 7496    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7497        editor.context_menu_next(&Default::default(), cx);
 7498        editor
 7499            .confirm_completion(&ConfirmCompletion::default(), cx)
 7500            .unwrap()
 7501    });
 7502    cx.assert_editor_state(indoc! {"
 7503        one.second_completionˇ
 7504        two
 7505        three
 7506    "});
 7507
 7508    handle_resolve_completion_request(
 7509        &mut cx,
 7510        Some(vec![
 7511            (
 7512                //This overlaps with the primary completion edit which is
 7513                //misbehavior from the LSP spec, test that we filter it out
 7514                indoc! {"
 7515                    one.second_ˇcompletion
 7516                    two
 7517                    threeˇ
 7518                "},
 7519                "overlapping additional edit",
 7520            ),
 7521            (
 7522                indoc! {"
 7523                    one.second_completion
 7524                    two
 7525                    threeˇ
 7526                "},
 7527                "\nadditional edit",
 7528            ),
 7529        ]),
 7530    )
 7531    .await;
 7532    apply_additional_edits.await.unwrap();
 7533    cx.assert_editor_state(indoc! {"
 7534        one.second_completionˇ
 7535        two
 7536        three
 7537        additional edit
 7538    "});
 7539
 7540    cx.set_state(indoc! {"
 7541        one.second_completion
 7542        twoˇ
 7543        threeˇ
 7544        additional edit
 7545    "});
 7546    cx.simulate_keystroke(" ");
 7547    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7548    cx.simulate_keystroke("s");
 7549    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7550
 7551    cx.assert_editor_state(indoc! {"
 7552        one.second_completion
 7553        two sˇ
 7554        three sˇ
 7555        additional edit
 7556    "});
 7557    handle_completion_request(
 7558        &mut cx,
 7559        indoc! {"
 7560            one.second_completion
 7561            two s
 7562            three <s|>
 7563            additional edit
 7564        "},
 7565        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7566        counter.clone(),
 7567    )
 7568    .await;
 7569    cx.condition(|editor, _| editor.context_menu_visible())
 7570        .await;
 7571    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7572
 7573    cx.simulate_keystroke("i");
 7574
 7575    handle_completion_request(
 7576        &mut cx,
 7577        indoc! {"
 7578            one.second_completion
 7579            two si
 7580            three <si|>
 7581            additional edit
 7582        "},
 7583        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7584        counter.clone(),
 7585    )
 7586    .await;
 7587    cx.condition(|editor, _| editor.context_menu_visible())
 7588        .await;
 7589    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7590
 7591    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7592        editor
 7593            .confirm_completion(&ConfirmCompletion::default(), cx)
 7594            .unwrap()
 7595    });
 7596    cx.assert_editor_state(indoc! {"
 7597        one.second_completion
 7598        two sixth_completionˇ
 7599        three sixth_completionˇ
 7600        additional edit
 7601    "});
 7602
 7603    handle_resolve_completion_request(&mut cx, None).await;
 7604    apply_additional_edits.await.unwrap();
 7605
 7606    _ = cx.update(|cx| {
 7607        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7608            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7609                settings.show_completions_on_input = Some(false);
 7610            });
 7611        })
 7612    });
 7613    cx.set_state("editorˇ");
 7614    cx.simulate_keystroke(".");
 7615    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7616    cx.simulate_keystroke("c");
 7617    cx.simulate_keystroke("l");
 7618    cx.simulate_keystroke("o");
 7619    cx.assert_editor_state("editor.cloˇ");
 7620    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7621    cx.update_editor(|editor, cx| {
 7622        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7623    });
 7624    handle_completion_request(
 7625        &mut cx,
 7626        "editor.<clo|>",
 7627        vec!["close", "clobber"],
 7628        counter.clone(),
 7629    )
 7630    .await;
 7631    cx.condition(|editor, _| editor.context_menu_visible())
 7632        .await;
 7633    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7634
 7635    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7636        editor
 7637            .confirm_completion(&ConfirmCompletion::default(), cx)
 7638            .unwrap()
 7639    });
 7640    cx.assert_editor_state("editor.closeˇ");
 7641    handle_resolve_completion_request(&mut cx, None).await;
 7642    apply_additional_edits.await.unwrap();
 7643}
 7644
 7645#[gpui::test]
 7646async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7647    init_test(cx, |_| {});
 7648    let mut cx = EditorLspTestContext::new_rust(
 7649        lsp::ServerCapabilities {
 7650            completion_provider: Some(lsp::CompletionOptions {
 7651                trigger_characters: Some(vec![".".to_string()]),
 7652                ..Default::default()
 7653            }),
 7654            ..Default::default()
 7655        },
 7656        cx,
 7657    )
 7658    .await;
 7659    cx.lsp
 7660        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7661            Ok(Some(lsp::CompletionResponse::Array(vec![
 7662                lsp::CompletionItem {
 7663                    label: "first".into(),
 7664                    ..Default::default()
 7665                },
 7666                lsp::CompletionItem {
 7667                    label: "last".into(),
 7668                    ..Default::default()
 7669                },
 7670            ])))
 7671        });
 7672    cx.set_state("variableˇ");
 7673    cx.simulate_keystroke(".");
 7674    cx.executor().run_until_parked();
 7675
 7676    cx.update_editor(|editor, _| {
 7677        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7678            assert_eq!(
 7679                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7680                &["first", "last"]
 7681            );
 7682        } else {
 7683            panic!("expected completion menu to be open");
 7684        }
 7685    });
 7686
 7687    cx.update_editor(|editor, cx| {
 7688        editor.move_page_down(&MovePageDown::default(), cx);
 7689        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7690            assert!(
 7691                menu.selected_item == 1,
 7692                "expected PageDown to select the last item from the context menu"
 7693            );
 7694        } else {
 7695            panic!("expected completion menu to stay open after PageDown");
 7696        }
 7697    });
 7698
 7699    cx.update_editor(|editor, cx| {
 7700        editor.move_page_up(&MovePageUp::default(), cx);
 7701        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7702            assert!(
 7703                menu.selected_item == 0,
 7704                "expected PageUp to select the first item from the context menu"
 7705            );
 7706        } else {
 7707            panic!("expected completion menu to stay open after PageUp");
 7708        }
 7709    });
 7710}
 7711
 7712#[gpui::test]
 7713async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7714    init_test(cx, |_| {});
 7715
 7716    let mut cx = EditorLspTestContext::new_rust(
 7717        lsp::ServerCapabilities {
 7718            completion_provider: Some(lsp::CompletionOptions {
 7719                trigger_characters: Some(vec![".".to_string()]),
 7720                resolve_provider: Some(true),
 7721                ..Default::default()
 7722            }),
 7723            ..Default::default()
 7724        },
 7725        cx,
 7726    )
 7727    .await;
 7728
 7729    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7730    cx.simulate_keystroke(".");
 7731    let completion_item = lsp::CompletionItem {
 7732        label: "Some".into(),
 7733        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7734        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7735        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7736            kind: lsp::MarkupKind::Markdown,
 7737            value: "```rust\nSome(2)\n```".to_string(),
 7738        })),
 7739        deprecated: Some(false),
 7740        sort_text: Some("Some".to_string()),
 7741        filter_text: Some("Some".to_string()),
 7742        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7743        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7744            range: lsp::Range {
 7745                start: lsp::Position {
 7746                    line: 0,
 7747                    character: 22,
 7748                },
 7749                end: lsp::Position {
 7750                    line: 0,
 7751                    character: 22,
 7752                },
 7753            },
 7754            new_text: "Some(2)".to_string(),
 7755        })),
 7756        additional_text_edits: Some(vec![lsp::TextEdit {
 7757            range: lsp::Range {
 7758                start: lsp::Position {
 7759                    line: 0,
 7760                    character: 20,
 7761                },
 7762                end: lsp::Position {
 7763                    line: 0,
 7764                    character: 22,
 7765                },
 7766            },
 7767            new_text: "".to_string(),
 7768        }]),
 7769        ..Default::default()
 7770    };
 7771
 7772    let closure_completion_item = completion_item.clone();
 7773    let counter = Arc::new(AtomicUsize::new(0));
 7774    let counter_clone = counter.clone();
 7775    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7776        let task_completion_item = closure_completion_item.clone();
 7777        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7778        async move {
 7779            Ok(Some(lsp::CompletionResponse::Array(vec![
 7780                task_completion_item,
 7781            ])))
 7782        }
 7783    });
 7784
 7785    cx.condition(|editor, _| editor.context_menu_visible())
 7786        .await;
 7787    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7788    assert!(request.next().await.is_some());
 7789    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7790
 7791    cx.simulate_keystroke("S");
 7792    cx.simulate_keystroke("o");
 7793    cx.simulate_keystroke("m");
 7794    cx.condition(|editor, _| editor.context_menu_visible())
 7795        .await;
 7796    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7797    assert!(request.next().await.is_some());
 7798    assert!(request.next().await.is_some());
 7799    assert!(request.next().await.is_some());
 7800    request.close();
 7801    assert!(request.next().await.is_none());
 7802    assert_eq!(
 7803        counter.load(atomic::Ordering::Acquire),
 7804        4,
 7805        "With the completions menu open, only one LSP request should happen per input"
 7806    );
 7807}
 7808
 7809#[gpui::test]
 7810async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7811    init_test(cx, |_| {});
 7812    let mut cx = EditorTestContext::new(cx).await;
 7813    let language = Arc::new(Language::new(
 7814        LanguageConfig {
 7815            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7816            ..Default::default()
 7817        },
 7818        Some(tree_sitter_rust::language()),
 7819    ));
 7820    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7821
 7822    // If multiple selections intersect a line, the line is only toggled once.
 7823    cx.set_state(indoc! {"
 7824        fn a() {
 7825            «//b();
 7826            ˇ»// «c();
 7827            //ˇ»  d();
 7828        }
 7829    "});
 7830
 7831    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7832
 7833    cx.assert_editor_state(indoc! {"
 7834        fn a() {
 7835            «b();
 7836            c();
 7837            ˇ» d();
 7838        }
 7839    "});
 7840
 7841    // The comment prefix is inserted at the same column for every line in a
 7842    // selection.
 7843    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7844
 7845    cx.assert_editor_state(indoc! {"
 7846        fn a() {
 7847            // «b();
 7848            // c();
 7849            ˇ»//  d();
 7850        }
 7851    "});
 7852
 7853    // If a selection ends at the beginning of a line, that line is not toggled.
 7854    cx.set_selections_state(indoc! {"
 7855        fn a() {
 7856            // b();
 7857            «// c();
 7858        ˇ»    //  d();
 7859        }
 7860    "});
 7861
 7862    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7863
 7864    cx.assert_editor_state(indoc! {"
 7865        fn a() {
 7866            // b();
 7867            «c();
 7868        ˇ»    //  d();
 7869        }
 7870    "});
 7871
 7872    // If a selection span a single line and is empty, the line is toggled.
 7873    cx.set_state(indoc! {"
 7874        fn a() {
 7875            a();
 7876            b();
 7877        ˇ
 7878        }
 7879    "});
 7880
 7881    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7882
 7883    cx.assert_editor_state(indoc! {"
 7884        fn a() {
 7885            a();
 7886            b();
 7887        //•ˇ
 7888        }
 7889    "});
 7890
 7891    // If a selection span multiple lines, empty lines are not toggled.
 7892    cx.set_state(indoc! {"
 7893        fn a() {
 7894            «a();
 7895
 7896            c();ˇ»
 7897        }
 7898    "});
 7899
 7900    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7901
 7902    cx.assert_editor_state(indoc! {"
 7903        fn a() {
 7904            // «a();
 7905
 7906            // c();ˇ»
 7907        }
 7908    "});
 7909
 7910    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7911    cx.set_state(indoc! {"
 7912        fn a() {
 7913            «// a();
 7914            /// b();
 7915            //! c();ˇ»
 7916        }
 7917    "});
 7918
 7919    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7920
 7921    cx.assert_editor_state(indoc! {"
 7922        fn a() {
 7923            «a();
 7924            b();
 7925            c();ˇ»
 7926        }
 7927    "});
 7928}
 7929
 7930#[gpui::test]
 7931async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7932    init_test(cx, |_| {});
 7933
 7934    let language = Arc::new(Language::new(
 7935        LanguageConfig {
 7936            line_comments: vec!["// ".into()],
 7937            ..Default::default()
 7938        },
 7939        Some(tree_sitter_rust::language()),
 7940    ));
 7941
 7942    let mut cx = EditorTestContext::new(cx).await;
 7943
 7944    cx.language_registry().add(language.clone());
 7945    cx.update_buffer(|buffer, cx| {
 7946        buffer.set_language(Some(language), cx);
 7947    });
 7948
 7949    let toggle_comments = &ToggleComments {
 7950        advance_downwards: true,
 7951    };
 7952
 7953    // Single cursor on one line -> advance
 7954    // Cursor moves horizontally 3 characters as well on non-blank line
 7955    cx.set_state(indoc!(
 7956        "fn a() {
 7957             ˇdog();
 7958             cat();
 7959        }"
 7960    ));
 7961    cx.update_editor(|editor, cx| {
 7962        editor.toggle_comments(toggle_comments, cx);
 7963    });
 7964    cx.assert_editor_state(indoc!(
 7965        "fn a() {
 7966             // dog();
 7967             catˇ();
 7968        }"
 7969    ));
 7970
 7971    // Single selection on one line -> don't advance
 7972    cx.set_state(indoc!(
 7973        "fn a() {
 7974             «dog()ˇ»;
 7975             cat();
 7976        }"
 7977    ));
 7978    cx.update_editor(|editor, cx| {
 7979        editor.toggle_comments(toggle_comments, cx);
 7980    });
 7981    cx.assert_editor_state(indoc!(
 7982        "fn a() {
 7983             // «dog()ˇ»;
 7984             cat();
 7985        }"
 7986    ));
 7987
 7988    // Multiple cursors on one line -> advance
 7989    cx.set_state(indoc!(
 7990        "fn a() {
 7991             ˇdˇog();
 7992             cat();
 7993        }"
 7994    ));
 7995    cx.update_editor(|editor, cx| {
 7996        editor.toggle_comments(toggle_comments, cx);
 7997    });
 7998    cx.assert_editor_state(indoc!(
 7999        "fn a() {
 8000             // dog();
 8001             catˇ(ˇ);
 8002        }"
 8003    ));
 8004
 8005    // Multiple cursors on one line, with selection -> don't advance
 8006    cx.set_state(indoc!(
 8007        "fn a() {
 8008             ˇdˇog«()ˇ»;
 8009             cat();
 8010        }"
 8011    ));
 8012    cx.update_editor(|editor, cx| {
 8013        editor.toggle_comments(toggle_comments, cx);
 8014    });
 8015    cx.assert_editor_state(indoc!(
 8016        "fn a() {
 8017             // ˇdˇog«()ˇ»;
 8018             cat();
 8019        }"
 8020    ));
 8021
 8022    // Single cursor on one line -> advance
 8023    // Cursor moves to column 0 on blank line
 8024    cx.set_state(indoc!(
 8025        "fn a() {
 8026             ˇdog();
 8027
 8028             cat();
 8029        }"
 8030    ));
 8031    cx.update_editor(|editor, cx| {
 8032        editor.toggle_comments(toggle_comments, cx);
 8033    });
 8034    cx.assert_editor_state(indoc!(
 8035        "fn a() {
 8036             // dog();
 8037        ˇ
 8038             cat();
 8039        }"
 8040    ));
 8041
 8042    // Single cursor on one line -> advance
 8043    // Cursor starts and ends at column 0
 8044    cx.set_state(indoc!(
 8045        "fn a() {
 8046         ˇ    dog();
 8047             cat();
 8048        }"
 8049    ));
 8050    cx.update_editor(|editor, cx| {
 8051        editor.toggle_comments(toggle_comments, cx);
 8052    });
 8053    cx.assert_editor_state(indoc!(
 8054        "fn a() {
 8055             // dog();
 8056         ˇ    cat();
 8057        }"
 8058    ));
 8059}
 8060
 8061#[gpui::test]
 8062async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8063    init_test(cx, |_| {});
 8064
 8065    let mut cx = EditorTestContext::new(cx).await;
 8066
 8067    let html_language = Arc::new(
 8068        Language::new(
 8069            LanguageConfig {
 8070                name: "HTML".into(),
 8071                block_comment: Some(("<!-- ".into(), " -->".into())),
 8072                ..Default::default()
 8073            },
 8074            Some(tree_sitter_html::language()),
 8075        )
 8076        .with_injection_query(
 8077            r#"
 8078            (script_element
 8079                (raw_text) @content
 8080                (#set! "language" "javascript"))
 8081            "#,
 8082        )
 8083        .unwrap(),
 8084    );
 8085
 8086    let javascript_language = Arc::new(Language::new(
 8087        LanguageConfig {
 8088            name: "JavaScript".into(),
 8089            line_comments: vec!["// ".into()],
 8090            ..Default::default()
 8091        },
 8092        Some(tree_sitter_typescript::language_tsx()),
 8093    ));
 8094
 8095    cx.language_registry().add(html_language.clone());
 8096    cx.language_registry().add(javascript_language.clone());
 8097    cx.update_buffer(|buffer, cx| {
 8098        buffer.set_language(Some(html_language), cx);
 8099    });
 8100
 8101    // Toggle comments for empty selections
 8102    cx.set_state(
 8103        &r#"
 8104            <p>A</p>ˇ
 8105            <p>B</p>ˇ
 8106            <p>C</p>ˇ
 8107        "#
 8108        .unindent(),
 8109    );
 8110    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8111    cx.assert_editor_state(
 8112        &r#"
 8113            <!-- <p>A</p>ˇ -->
 8114            <!-- <p>B</p>ˇ -->
 8115            <!-- <p>C</p>ˇ -->
 8116        "#
 8117        .unindent(),
 8118    );
 8119    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8120    cx.assert_editor_state(
 8121        &r#"
 8122            <p>A</p>ˇ
 8123            <p>B</p>ˇ
 8124            <p>C</p>ˇ
 8125        "#
 8126        .unindent(),
 8127    );
 8128
 8129    // Toggle comments for mixture of empty and non-empty selections, where
 8130    // multiple selections occupy a given line.
 8131    cx.set_state(
 8132        &r#"
 8133            <p>A«</p>
 8134            <p>ˇ»B</p>ˇ
 8135            <p>C«</p>
 8136            <p>ˇ»D</p>ˇ
 8137        "#
 8138        .unindent(),
 8139    );
 8140
 8141    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8142    cx.assert_editor_state(
 8143        &r#"
 8144            <!-- <p>A«</p>
 8145            <p>ˇ»B</p>ˇ -->
 8146            <!-- <p>C«</p>
 8147            <p>ˇ»D</p>ˇ -->
 8148        "#
 8149        .unindent(),
 8150    );
 8151    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8152    cx.assert_editor_state(
 8153        &r#"
 8154            <p>A«</p>
 8155            <p>ˇ»B</p>ˇ
 8156            <p>C«</p>
 8157            <p>ˇ»D</p>ˇ
 8158        "#
 8159        .unindent(),
 8160    );
 8161
 8162    // Toggle comments when different languages are active for different
 8163    // selections.
 8164    cx.set_state(
 8165        &r#"
 8166            ˇ<script>
 8167                ˇvar x = new Y();
 8168            ˇ</script>
 8169        "#
 8170        .unindent(),
 8171    );
 8172    cx.executor().run_until_parked();
 8173    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8174    cx.assert_editor_state(
 8175        &r#"
 8176            <!-- ˇ<script> -->
 8177                // ˇvar x = new Y();
 8178            <!-- ˇ</script> -->
 8179        "#
 8180        .unindent(),
 8181    );
 8182}
 8183
 8184#[gpui::test]
 8185fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8186    init_test(cx, |_| {});
 8187
 8188    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8189    let multibuffer = cx.new_model(|cx| {
 8190        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8191        multibuffer.push_excerpts(
 8192            buffer.clone(),
 8193            [
 8194                ExcerptRange {
 8195                    context: Point::new(0, 0)..Point::new(0, 4),
 8196                    primary: None,
 8197                },
 8198                ExcerptRange {
 8199                    context: Point::new(1, 0)..Point::new(1, 4),
 8200                    primary: None,
 8201                },
 8202            ],
 8203            cx,
 8204        );
 8205        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8206        multibuffer
 8207    });
 8208
 8209    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8210    _ = view.update(cx, |view, cx| {
 8211        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8212        view.change_selections(None, cx, |s| {
 8213            s.select_ranges([
 8214                Point::new(0, 0)..Point::new(0, 0),
 8215                Point::new(1, 0)..Point::new(1, 0),
 8216            ])
 8217        });
 8218
 8219        view.handle_input("X", cx);
 8220        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8221        assert_eq!(
 8222            view.selections.ranges(cx),
 8223            [
 8224                Point::new(0, 1)..Point::new(0, 1),
 8225                Point::new(1, 1)..Point::new(1, 1),
 8226            ]
 8227        );
 8228
 8229        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8230        view.change_selections(None, cx, |s| {
 8231            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8232        });
 8233        view.backspace(&Default::default(), cx);
 8234        assert_eq!(view.text(cx), "Xa\nbbb");
 8235        assert_eq!(
 8236            view.selections.ranges(cx),
 8237            [Point::new(1, 0)..Point::new(1, 0)]
 8238        );
 8239
 8240        view.change_selections(None, cx, |s| {
 8241            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8242        });
 8243        view.backspace(&Default::default(), cx);
 8244        assert_eq!(view.text(cx), "X\nbb");
 8245        assert_eq!(
 8246            view.selections.ranges(cx),
 8247            [Point::new(0, 1)..Point::new(0, 1)]
 8248        );
 8249    });
 8250}
 8251
 8252#[gpui::test]
 8253fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8254    init_test(cx, |_| {});
 8255
 8256    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8257    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8258        indoc! {"
 8259            [aaaa
 8260            (bbbb]
 8261            cccc)",
 8262        },
 8263        markers.clone(),
 8264    );
 8265    let excerpt_ranges = markers.into_iter().map(|marker| {
 8266        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8267        ExcerptRange {
 8268            context,
 8269            primary: None,
 8270        }
 8271    });
 8272    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8273    let multibuffer = cx.new_model(|cx| {
 8274        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8275        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8276        multibuffer
 8277    });
 8278
 8279    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8280    _ = view.update(cx, |view, cx| {
 8281        let (expected_text, selection_ranges) = marked_text_ranges(
 8282            indoc! {"
 8283                aaaa
 8284                bˇbbb
 8285                bˇbbˇb
 8286                cccc"
 8287            },
 8288            true,
 8289        );
 8290        assert_eq!(view.text(cx), expected_text);
 8291        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8292
 8293        view.handle_input("X", cx);
 8294
 8295        let (expected_text, expected_selections) = marked_text_ranges(
 8296            indoc! {"
 8297                aaaa
 8298                bXˇbbXb
 8299                bXˇbbXˇb
 8300                cccc"
 8301            },
 8302            false,
 8303        );
 8304        assert_eq!(view.text(cx), expected_text);
 8305        assert_eq!(view.selections.ranges(cx), expected_selections);
 8306
 8307        view.newline(&Newline, cx);
 8308        let (expected_text, expected_selections) = marked_text_ranges(
 8309            indoc! {"
 8310                aaaa
 8311                bX
 8312                ˇbbX
 8313                b
 8314                bX
 8315                ˇbbX
 8316                ˇb
 8317                cccc"
 8318            },
 8319            false,
 8320        );
 8321        assert_eq!(view.text(cx), expected_text);
 8322        assert_eq!(view.selections.ranges(cx), expected_selections);
 8323    });
 8324}
 8325
 8326#[gpui::test]
 8327fn test_refresh_selections(cx: &mut TestAppContext) {
 8328    init_test(cx, |_| {});
 8329
 8330    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8331    let mut excerpt1_id = None;
 8332    let multibuffer = cx.new_model(|cx| {
 8333        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8334        excerpt1_id = multibuffer
 8335            .push_excerpts(
 8336                buffer.clone(),
 8337                [
 8338                    ExcerptRange {
 8339                        context: Point::new(0, 0)..Point::new(1, 4),
 8340                        primary: None,
 8341                    },
 8342                    ExcerptRange {
 8343                        context: Point::new(1, 0)..Point::new(2, 4),
 8344                        primary: None,
 8345                    },
 8346                ],
 8347                cx,
 8348            )
 8349            .into_iter()
 8350            .next();
 8351        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8352        multibuffer
 8353    });
 8354
 8355    let editor = cx.add_window(|cx| {
 8356        let mut editor = build_editor(multibuffer.clone(), cx);
 8357        let snapshot = editor.snapshot(cx);
 8358        editor.change_selections(None, cx, |s| {
 8359            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8360        });
 8361        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8362        assert_eq!(
 8363            editor.selections.ranges(cx),
 8364            [
 8365                Point::new(1, 3)..Point::new(1, 3),
 8366                Point::new(2, 1)..Point::new(2, 1),
 8367            ]
 8368        );
 8369        editor
 8370    });
 8371
 8372    // Refreshing selections is a no-op when excerpts haven't changed.
 8373    _ = editor.update(cx, |editor, cx| {
 8374        editor.change_selections(None, cx, |s| s.refresh());
 8375        assert_eq!(
 8376            editor.selections.ranges(cx),
 8377            [
 8378                Point::new(1, 3)..Point::new(1, 3),
 8379                Point::new(2, 1)..Point::new(2, 1),
 8380            ]
 8381        );
 8382    });
 8383
 8384    _ = multibuffer.update(cx, |multibuffer, cx| {
 8385        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8386    });
 8387    _ = editor.update(cx, |editor, cx| {
 8388        // Removing an excerpt causes the first selection to become degenerate.
 8389        assert_eq!(
 8390            editor.selections.ranges(cx),
 8391            [
 8392                Point::new(0, 0)..Point::new(0, 0),
 8393                Point::new(0, 1)..Point::new(0, 1)
 8394            ]
 8395        );
 8396
 8397        // Refreshing selections will relocate the first selection to the original buffer
 8398        // location.
 8399        editor.change_selections(None, cx, |s| s.refresh());
 8400        assert_eq!(
 8401            editor.selections.ranges(cx),
 8402            [
 8403                Point::new(0, 1)..Point::new(0, 1),
 8404                Point::new(0, 3)..Point::new(0, 3)
 8405            ]
 8406        );
 8407        assert!(editor.selections.pending_anchor().is_some());
 8408    });
 8409}
 8410
 8411#[gpui::test]
 8412fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8413    init_test(cx, |_| {});
 8414
 8415    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8416    let mut excerpt1_id = None;
 8417    let multibuffer = cx.new_model(|cx| {
 8418        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8419        excerpt1_id = multibuffer
 8420            .push_excerpts(
 8421                buffer.clone(),
 8422                [
 8423                    ExcerptRange {
 8424                        context: Point::new(0, 0)..Point::new(1, 4),
 8425                        primary: None,
 8426                    },
 8427                    ExcerptRange {
 8428                        context: Point::new(1, 0)..Point::new(2, 4),
 8429                        primary: None,
 8430                    },
 8431                ],
 8432                cx,
 8433            )
 8434            .into_iter()
 8435            .next();
 8436        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8437        multibuffer
 8438    });
 8439
 8440    let editor = cx.add_window(|cx| {
 8441        let mut editor = build_editor(multibuffer.clone(), cx);
 8442        let snapshot = editor.snapshot(cx);
 8443        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8444        assert_eq!(
 8445            editor.selections.ranges(cx),
 8446            [Point::new(1, 3)..Point::new(1, 3)]
 8447        );
 8448        editor
 8449    });
 8450
 8451    _ = multibuffer.update(cx, |multibuffer, cx| {
 8452        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8453    });
 8454    _ = editor.update(cx, |editor, cx| {
 8455        assert_eq!(
 8456            editor.selections.ranges(cx),
 8457            [Point::new(0, 0)..Point::new(0, 0)]
 8458        );
 8459
 8460        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8461        editor.change_selections(None, cx, |s| s.refresh());
 8462        assert_eq!(
 8463            editor.selections.ranges(cx),
 8464            [Point::new(0, 3)..Point::new(0, 3)]
 8465        );
 8466        assert!(editor.selections.pending_anchor().is_some());
 8467    });
 8468}
 8469
 8470#[gpui::test]
 8471async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8472    init_test(cx, |_| {});
 8473
 8474    let language = Arc::new(
 8475        Language::new(
 8476            LanguageConfig {
 8477                brackets: BracketPairConfig {
 8478                    pairs: vec![
 8479                        BracketPair {
 8480                            start: "{".to_string(),
 8481                            end: "}".to_string(),
 8482                            close: true,
 8483                            surround: true,
 8484                            newline: true,
 8485                        },
 8486                        BracketPair {
 8487                            start: "/* ".to_string(),
 8488                            end: " */".to_string(),
 8489                            close: true,
 8490                            surround: true,
 8491                            newline: true,
 8492                        },
 8493                    ],
 8494                    ..Default::default()
 8495                },
 8496                ..Default::default()
 8497            },
 8498            Some(tree_sitter_rust::language()),
 8499        )
 8500        .with_indents_query("")
 8501        .unwrap(),
 8502    );
 8503
 8504    let text = concat!(
 8505        "{   }\n",     //
 8506        "  x\n",       //
 8507        "  /*   */\n", //
 8508        "x\n",         //
 8509        "{{} }\n",     //
 8510    );
 8511
 8512    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8513    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8514    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8515    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8516        .await;
 8517
 8518    _ = view.update(cx, |view, cx| {
 8519        view.change_selections(None, cx, |s| {
 8520            s.select_display_ranges([
 8521                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8522                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8523                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8524            ])
 8525        });
 8526        view.newline(&Newline, cx);
 8527
 8528        assert_eq!(
 8529            view.buffer().read(cx).read(cx).text(),
 8530            concat!(
 8531                "{ \n",    // Suppress rustfmt
 8532                "\n",      //
 8533                "}\n",     //
 8534                "  x\n",   //
 8535                "  /* \n", //
 8536                "  \n",    //
 8537                "  */\n",  //
 8538                "x\n",     //
 8539                "{{} \n",  //
 8540                "}\n",     //
 8541            )
 8542        );
 8543    });
 8544}
 8545
 8546#[gpui::test]
 8547fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8548    init_test(cx, |_| {});
 8549
 8550    let editor = cx.add_window(|cx| {
 8551        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8552        build_editor(buffer.clone(), cx)
 8553    });
 8554
 8555    _ = editor.update(cx, |editor, cx| {
 8556        struct Type1;
 8557        struct Type2;
 8558
 8559        let buffer = editor.buffer.read(cx).snapshot(cx);
 8560
 8561        let anchor_range =
 8562            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8563
 8564        editor.highlight_background::<Type1>(
 8565            &[
 8566                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8567                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8568                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8569                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8570            ],
 8571            |_| Hsla::red(),
 8572            cx,
 8573        );
 8574        editor.highlight_background::<Type2>(
 8575            &[
 8576                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8577                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8578                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8579                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8580            ],
 8581            |_| Hsla::green(),
 8582            cx,
 8583        );
 8584
 8585        let snapshot = editor.snapshot(cx);
 8586        let mut highlighted_ranges = editor.background_highlights_in_range(
 8587            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8588            &snapshot,
 8589            cx.theme().colors(),
 8590        );
 8591        // Enforce a consistent ordering based on color without relying on the ordering of the
 8592        // highlight's `TypeId` which is non-executor.
 8593        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8594        assert_eq!(
 8595            highlighted_ranges,
 8596            &[
 8597                (
 8598                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8599                    Hsla::red(),
 8600                ),
 8601                (
 8602                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8603                    Hsla::red(),
 8604                ),
 8605                (
 8606                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8607                    Hsla::green(),
 8608                ),
 8609                (
 8610                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8611                    Hsla::green(),
 8612                ),
 8613            ]
 8614        );
 8615        assert_eq!(
 8616            editor.background_highlights_in_range(
 8617                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8618                &snapshot,
 8619                cx.theme().colors(),
 8620            ),
 8621            &[(
 8622                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8623                Hsla::red(),
 8624            )]
 8625        );
 8626    });
 8627}
 8628
 8629#[gpui::test]
 8630async fn test_following(cx: &mut gpui::TestAppContext) {
 8631    init_test(cx, |_| {});
 8632
 8633    let fs = FakeFs::new(cx.executor());
 8634    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8635
 8636    let buffer = project.update(cx, |project, cx| {
 8637        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8638        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8639    });
 8640    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8641    let follower = cx.update(|cx| {
 8642        cx.open_window(
 8643            WindowOptions {
 8644                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8645                    gpui::Point::new(px(0.), px(0.)),
 8646                    gpui::Point::new(px(10.), px(80.)),
 8647                ))),
 8648                ..Default::default()
 8649            },
 8650            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8651        )
 8652        .unwrap()
 8653    });
 8654
 8655    let is_still_following = Rc::new(RefCell::new(true));
 8656    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8657    let pending_update = Rc::new(RefCell::new(None));
 8658    _ = follower.update(cx, {
 8659        let update = pending_update.clone();
 8660        let is_still_following = is_still_following.clone();
 8661        let follower_edit_event_count = follower_edit_event_count.clone();
 8662        |_, cx| {
 8663            cx.subscribe(
 8664                &leader.root_view(cx).unwrap(),
 8665                move |_, leader, event, cx| {
 8666                    leader
 8667                        .read(cx)
 8668                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8669                },
 8670            )
 8671            .detach();
 8672
 8673            cx.subscribe(
 8674                &follower.root_view(cx).unwrap(),
 8675                move |_, _, event: &EditorEvent, _cx| {
 8676                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8677                        *is_still_following.borrow_mut() = false;
 8678                    }
 8679
 8680                    if let EditorEvent::BufferEdited = event {
 8681                        *follower_edit_event_count.borrow_mut() += 1;
 8682                    }
 8683                },
 8684            )
 8685            .detach();
 8686        }
 8687    });
 8688
 8689    // Update the selections only
 8690    _ = leader.update(cx, |leader, cx| {
 8691        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8692    });
 8693    follower
 8694        .update(cx, |follower, cx| {
 8695            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8696        })
 8697        .unwrap()
 8698        .await
 8699        .unwrap();
 8700    _ = follower.update(cx, |follower, cx| {
 8701        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8702    });
 8703    assert_eq!(*is_still_following.borrow(), true);
 8704    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8705
 8706    // Update the scroll position only
 8707    _ = leader.update(cx, |leader, cx| {
 8708        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8709    });
 8710    follower
 8711        .update(cx, |follower, cx| {
 8712            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8713        })
 8714        .unwrap()
 8715        .await
 8716        .unwrap();
 8717    assert_eq!(
 8718        follower
 8719            .update(cx, |follower, cx| follower.scroll_position(cx))
 8720            .unwrap(),
 8721        gpui::Point::new(1.5, 3.5)
 8722    );
 8723    assert_eq!(*is_still_following.borrow(), true);
 8724    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8725
 8726    // Update the selections and scroll position. The follower's scroll position is updated
 8727    // via autoscroll, not via the leader's exact scroll position.
 8728    _ = leader.update(cx, |leader, cx| {
 8729        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8730        leader.request_autoscroll(Autoscroll::newest(), cx);
 8731        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8732    });
 8733    follower
 8734        .update(cx, |follower, cx| {
 8735            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8736        })
 8737        .unwrap()
 8738        .await
 8739        .unwrap();
 8740    _ = follower.update(cx, |follower, cx| {
 8741        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8742        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8743    });
 8744    assert_eq!(*is_still_following.borrow(), true);
 8745
 8746    // Creating a pending selection that precedes another selection
 8747    _ = leader.update(cx, |leader, cx| {
 8748        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8749        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8750    });
 8751    follower
 8752        .update(cx, |follower, cx| {
 8753            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8754        })
 8755        .unwrap()
 8756        .await
 8757        .unwrap();
 8758    _ = follower.update(cx, |follower, cx| {
 8759        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8760    });
 8761    assert_eq!(*is_still_following.borrow(), true);
 8762
 8763    // Extend the pending selection so that it surrounds another selection
 8764    _ = leader.update(cx, |leader, cx| {
 8765        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8766    });
 8767    follower
 8768        .update(cx, |follower, cx| {
 8769            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8770        })
 8771        .unwrap()
 8772        .await
 8773        .unwrap();
 8774    _ = follower.update(cx, |follower, cx| {
 8775        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8776    });
 8777
 8778    // Scrolling locally breaks the follow
 8779    _ = follower.update(cx, |follower, cx| {
 8780        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8781        follower.set_scroll_anchor(
 8782            ScrollAnchor {
 8783                anchor: top_anchor,
 8784                offset: gpui::Point::new(0.0, 0.5),
 8785            },
 8786            cx,
 8787        );
 8788    });
 8789    assert_eq!(*is_still_following.borrow(), false);
 8790}
 8791
 8792#[gpui::test]
 8793async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8794    init_test(cx, |_| {});
 8795
 8796    let fs = FakeFs::new(cx.executor());
 8797    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8798    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8799    let pane = workspace
 8800        .update(cx, |workspace, _| workspace.active_pane().clone())
 8801        .unwrap();
 8802
 8803    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8804
 8805    let leader = pane.update(cx, |_, cx| {
 8806        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8807        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8808    });
 8809
 8810    // Start following the editor when it has no excerpts.
 8811    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8812    let follower_1 = cx
 8813        .update_window(*workspace.deref(), |_, cx| {
 8814            Editor::from_state_proto(
 8815                workspace.root_view(cx).unwrap(),
 8816                ViewId {
 8817                    creator: Default::default(),
 8818                    id: 0,
 8819                },
 8820                &mut state_message,
 8821                cx,
 8822            )
 8823        })
 8824        .unwrap()
 8825        .unwrap()
 8826        .await
 8827        .unwrap();
 8828
 8829    let update_message = Rc::new(RefCell::new(None));
 8830    follower_1.update(cx, {
 8831        let update = update_message.clone();
 8832        |_, cx| {
 8833            cx.subscribe(&leader, move |_, leader, event, cx| {
 8834                leader
 8835                    .read(cx)
 8836                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8837            })
 8838            .detach();
 8839        }
 8840    });
 8841
 8842    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8843        (
 8844            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8845            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8846        )
 8847    });
 8848
 8849    // Insert some excerpts.
 8850    _ = leader.update(cx, |leader, cx| {
 8851        leader.buffer.update(cx, |multibuffer, cx| {
 8852            let excerpt_ids = multibuffer.push_excerpts(
 8853                buffer_1.clone(),
 8854                [
 8855                    ExcerptRange {
 8856                        context: 1..6,
 8857                        primary: None,
 8858                    },
 8859                    ExcerptRange {
 8860                        context: 12..15,
 8861                        primary: None,
 8862                    },
 8863                    ExcerptRange {
 8864                        context: 0..3,
 8865                        primary: None,
 8866                    },
 8867                ],
 8868                cx,
 8869            );
 8870            multibuffer.insert_excerpts_after(
 8871                excerpt_ids[0],
 8872                buffer_2.clone(),
 8873                [
 8874                    ExcerptRange {
 8875                        context: 8..12,
 8876                        primary: None,
 8877                    },
 8878                    ExcerptRange {
 8879                        context: 0..6,
 8880                        primary: None,
 8881                    },
 8882                ],
 8883                cx,
 8884            );
 8885        });
 8886    });
 8887
 8888    // Apply the update of adding the excerpts.
 8889    follower_1
 8890        .update(cx, |follower, cx| {
 8891            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8892        })
 8893        .await
 8894        .unwrap();
 8895    assert_eq!(
 8896        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8897        leader.update(cx, |editor, cx| editor.text(cx))
 8898    );
 8899    update_message.borrow_mut().take();
 8900
 8901    // Start following separately after it already has excerpts.
 8902    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8903    let follower_2 = cx
 8904        .update_window(*workspace.deref(), |_, cx| {
 8905            Editor::from_state_proto(
 8906                workspace.root_view(cx).unwrap().clone(),
 8907                ViewId {
 8908                    creator: Default::default(),
 8909                    id: 0,
 8910                },
 8911                &mut state_message,
 8912                cx,
 8913            )
 8914        })
 8915        .unwrap()
 8916        .unwrap()
 8917        .await
 8918        .unwrap();
 8919    assert_eq!(
 8920        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8921        leader.update(cx, |editor, cx| editor.text(cx))
 8922    );
 8923
 8924    // Remove some excerpts.
 8925    _ = leader.update(cx, |leader, cx| {
 8926        leader.buffer.update(cx, |multibuffer, cx| {
 8927            let excerpt_ids = multibuffer.excerpt_ids();
 8928            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8929            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8930        });
 8931    });
 8932
 8933    // Apply the update of removing the excerpts.
 8934    follower_1
 8935        .update(cx, |follower, cx| {
 8936            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8937        })
 8938        .await
 8939        .unwrap();
 8940    follower_2
 8941        .update(cx, |follower, cx| {
 8942            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8943        })
 8944        .await
 8945        .unwrap();
 8946    update_message.borrow_mut().take();
 8947    assert_eq!(
 8948        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8949        leader.update(cx, |editor, cx| editor.text(cx))
 8950    );
 8951}
 8952
 8953#[gpui::test]
 8954async fn go_to_prev_overlapping_diagnostic(
 8955    executor: BackgroundExecutor,
 8956    cx: &mut gpui::TestAppContext,
 8957) {
 8958    init_test(cx, |_| {});
 8959
 8960    let mut cx = EditorTestContext::new(cx).await;
 8961    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 8962
 8963    cx.set_state(indoc! {"
 8964        ˇfn func(abc def: i32) -> u32 {
 8965        }
 8966    "});
 8967
 8968    _ = cx.update(|cx| {
 8969        _ = project.update(cx, |project, cx| {
 8970            project
 8971                .update_diagnostics(
 8972                    LanguageServerId(0),
 8973                    lsp::PublishDiagnosticsParams {
 8974                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 8975                        version: None,
 8976                        diagnostics: vec![
 8977                            lsp::Diagnostic {
 8978                                range: lsp::Range::new(
 8979                                    lsp::Position::new(0, 11),
 8980                                    lsp::Position::new(0, 12),
 8981                                ),
 8982                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8983                                ..Default::default()
 8984                            },
 8985                            lsp::Diagnostic {
 8986                                range: lsp::Range::new(
 8987                                    lsp::Position::new(0, 12),
 8988                                    lsp::Position::new(0, 15),
 8989                                ),
 8990                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8991                                ..Default::default()
 8992                            },
 8993                            lsp::Diagnostic {
 8994                                range: lsp::Range::new(
 8995                                    lsp::Position::new(0, 25),
 8996                                    lsp::Position::new(0, 28),
 8997                                ),
 8998                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8999                                ..Default::default()
 9000                            },
 9001                        ],
 9002                    },
 9003                    &[],
 9004                    cx,
 9005                )
 9006                .unwrap()
 9007        });
 9008    });
 9009
 9010    executor.run_until_parked();
 9011
 9012    cx.update_editor(|editor, cx| {
 9013        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9014    });
 9015
 9016    cx.assert_editor_state(indoc! {"
 9017        fn func(abc def: i32) -> ˇu32 {
 9018        }
 9019    "});
 9020
 9021    cx.update_editor(|editor, cx| {
 9022        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9023    });
 9024
 9025    cx.assert_editor_state(indoc! {"
 9026        fn func(abc ˇdef: i32) -> u32 {
 9027        }
 9028    "});
 9029
 9030    cx.update_editor(|editor, cx| {
 9031        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9032    });
 9033
 9034    cx.assert_editor_state(indoc! {"
 9035        fn func(abcˇ def: i32) -> u32 {
 9036        }
 9037    "});
 9038
 9039    cx.update_editor(|editor, cx| {
 9040        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9041    });
 9042
 9043    cx.assert_editor_state(indoc! {"
 9044        fn func(abc def: i32) -> ˇu32 {
 9045        }
 9046    "});
 9047}
 9048
 9049#[gpui::test]
 9050async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9051    init_test(cx, |_| {});
 9052
 9053    let mut cx = EditorTestContext::new(cx).await;
 9054
 9055    let diff_base = r#"
 9056        use some::mod;
 9057
 9058        const A: u32 = 42;
 9059
 9060        fn main() {
 9061            println!("hello");
 9062
 9063            println!("world");
 9064        }
 9065        "#
 9066    .unindent();
 9067
 9068    // Edits are modified, removed, modified, added
 9069    cx.set_state(
 9070        &r#"
 9071        use some::modified;
 9072
 9073        ˇ
 9074        fn main() {
 9075            println!("hello there");
 9076
 9077            println!("around the");
 9078            println!("world");
 9079        }
 9080        "#
 9081        .unindent(),
 9082    );
 9083
 9084    cx.set_diff_base(Some(&diff_base));
 9085    executor.run_until_parked();
 9086
 9087    cx.update_editor(|editor, cx| {
 9088        //Wrap around the bottom of the buffer
 9089        for _ in 0..3 {
 9090            editor.go_to_hunk(&GoToHunk, cx);
 9091        }
 9092    });
 9093
 9094    cx.assert_editor_state(
 9095        &r#"
 9096        ˇuse some::modified;
 9097
 9098
 9099        fn main() {
 9100            println!("hello there");
 9101
 9102            println!("around the");
 9103            println!("world");
 9104        }
 9105        "#
 9106        .unindent(),
 9107    );
 9108
 9109    cx.update_editor(|editor, cx| {
 9110        //Wrap around the top of the buffer
 9111        for _ in 0..2 {
 9112            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9113        }
 9114    });
 9115
 9116    cx.assert_editor_state(
 9117        &r#"
 9118        use some::modified;
 9119
 9120
 9121        fn main() {
 9122        ˇ    println!("hello there");
 9123
 9124            println!("around the");
 9125            println!("world");
 9126        }
 9127        "#
 9128        .unindent(),
 9129    );
 9130
 9131    cx.update_editor(|editor, cx| {
 9132        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9133    });
 9134
 9135    cx.assert_editor_state(
 9136        &r#"
 9137        use some::modified;
 9138
 9139        ˇ
 9140        fn main() {
 9141            println!("hello there");
 9142
 9143            println!("around the");
 9144            println!("world");
 9145        }
 9146        "#
 9147        .unindent(),
 9148    );
 9149
 9150    cx.update_editor(|editor, cx| {
 9151        for _ in 0..3 {
 9152            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9153        }
 9154    });
 9155
 9156    cx.assert_editor_state(
 9157        &r#"
 9158        use some::modified;
 9159
 9160
 9161        fn main() {
 9162        ˇ    println!("hello there");
 9163
 9164            println!("around the");
 9165            println!("world");
 9166        }
 9167        "#
 9168        .unindent(),
 9169    );
 9170
 9171    cx.update_editor(|editor, cx| {
 9172        editor.fold(&Fold, cx);
 9173
 9174        //Make sure that the fold only gets one hunk
 9175        for _ in 0..4 {
 9176            editor.go_to_hunk(&GoToHunk, cx);
 9177        }
 9178    });
 9179
 9180    cx.assert_editor_state(
 9181        &r#"
 9182        ˇuse some::modified;
 9183
 9184
 9185        fn main() {
 9186            println!("hello there");
 9187
 9188            println!("around the");
 9189            println!("world");
 9190        }
 9191        "#
 9192        .unindent(),
 9193    );
 9194}
 9195
 9196#[test]
 9197fn test_split_words() {
 9198    fn split(text: &str) -> Vec<&str> {
 9199        split_words(text).collect()
 9200    }
 9201
 9202    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9203    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9204    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9205    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9206    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9207    assert_eq!(split("helloworld"), &["helloworld"]);
 9208
 9209    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9210}
 9211
 9212#[gpui::test]
 9213async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9214    init_test(cx, |_| {});
 9215
 9216    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9217    let mut assert = |before, after| {
 9218        let _state_context = cx.set_state(before);
 9219        cx.update_editor(|editor, cx| {
 9220            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9221        });
 9222        cx.assert_editor_state(after);
 9223    };
 9224
 9225    // Outside bracket jumps to outside of matching bracket
 9226    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9227    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9228
 9229    // Inside bracket jumps to inside of matching bracket
 9230    assert("console.log(ˇvar);", "console.log(varˇ);");
 9231    assert("console.log(varˇ);", "console.log(ˇvar);");
 9232
 9233    // When outside a bracket and inside, favor jumping to the inside bracket
 9234    assert(
 9235        "console.log('foo', [1, 2, 3]ˇ);",
 9236        "console.log(ˇ'foo', [1, 2, 3]);",
 9237    );
 9238    assert(
 9239        "console.log(ˇ'foo', [1, 2, 3]);",
 9240        "console.log('foo', [1, 2, 3]ˇ);",
 9241    );
 9242
 9243    // Bias forward if two options are equally likely
 9244    assert(
 9245        "let result = curried_fun()ˇ();",
 9246        "let result = curried_fun()()ˇ;",
 9247    );
 9248
 9249    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9250    assert(
 9251        indoc! {"
 9252            function test() {
 9253                console.log('test')ˇ
 9254            }"},
 9255        indoc! {"
 9256            function test() {
 9257                console.logˇ('test')
 9258            }"},
 9259    );
 9260}
 9261
 9262#[gpui::test]
 9263async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9264    init_test(cx, |_| {});
 9265
 9266    let fs = FakeFs::new(cx.executor());
 9267    fs.insert_tree(
 9268        "/a",
 9269        json!({
 9270            "main.rs": "fn main() { let a = 5; }",
 9271            "other.rs": "// Test file",
 9272        }),
 9273    )
 9274    .await;
 9275    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9276
 9277    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9278    language_registry.add(Arc::new(Language::new(
 9279        LanguageConfig {
 9280            name: "Rust".into(),
 9281            matcher: LanguageMatcher {
 9282                path_suffixes: vec!["rs".to_string()],
 9283                ..Default::default()
 9284            },
 9285            brackets: BracketPairConfig {
 9286                pairs: vec![BracketPair {
 9287                    start: "{".to_string(),
 9288                    end: "}".to_string(),
 9289                    close: true,
 9290                    surround: true,
 9291                    newline: true,
 9292                }],
 9293                disabled_scopes_by_bracket_ix: Vec::new(),
 9294            },
 9295            ..Default::default()
 9296        },
 9297        Some(tree_sitter_rust::language()),
 9298    )));
 9299    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9300        "Rust",
 9301        FakeLspAdapter {
 9302            capabilities: lsp::ServerCapabilities {
 9303                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9304                    first_trigger_character: "{".to_string(),
 9305                    more_trigger_character: None,
 9306                }),
 9307                ..Default::default()
 9308            },
 9309            ..Default::default()
 9310        },
 9311    );
 9312
 9313    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9314
 9315    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9316
 9317    let worktree_id = workspace
 9318        .update(cx, |workspace, cx| {
 9319            workspace.project().update(cx, |project, cx| {
 9320                project.worktrees().next().unwrap().read(cx).id()
 9321            })
 9322        })
 9323        .unwrap();
 9324
 9325    let buffer = project
 9326        .update(cx, |project, cx| {
 9327            project.open_local_buffer("/a/main.rs", cx)
 9328        })
 9329        .await
 9330        .unwrap();
 9331    cx.executor().run_until_parked();
 9332    cx.executor().start_waiting();
 9333    let fake_server = fake_servers.next().await.unwrap();
 9334    let editor_handle = workspace
 9335        .update(cx, |workspace, cx| {
 9336            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9337        })
 9338        .unwrap()
 9339        .await
 9340        .unwrap()
 9341        .downcast::<Editor>()
 9342        .unwrap();
 9343
 9344    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9345        assert_eq!(
 9346            params.text_document_position.text_document.uri,
 9347            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9348        );
 9349        assert_eq!(
 9350            params.text_document_position.position,
 9351            lsp::Position::new(0, 21),
 9352        );
 9353
 9354        Ok(Some(vec![lsp::TextEdit {
 9355            new_text: "]".to_string(),
 9356            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9357        }]))
 9358    });
 9359
 9360    editor_handle.update(cx, |editor, cx| {
 9361        editor.focus(cx);
 9362        editor.change_selections(None, cx, |s| {
 9363            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9364        });
 9365        editor.handle_input("{", cx);
 9366    });
 9367
 9368    cx.executor().run_until_parked();
 9369
 9370    _ = buffer.update(cx, |buffer, _| {
 9371        assert_eq!(
 9372            buffer.text(),
 9373            "fn main() { let a = {5}; }",
 9374            "No extra braces from on type formatting should appear in the buffer"
 9375        )
 9376    });
 9377}
 9378
 9379#[gpui::test]
 9380async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9381    init_test(cx, |_| {});
 9382
 9383    let fs = FakeFs::new(cx.executor());
 9384    fs.insert_tree(
 9385        "/a",
 9386        json!({
 9387            "main.rs": "fn main() { let a = 5; }",
 9388            "other.rs": "// Test file",
 9389        }),
 9390    )
 9391    .await;
 9392
 9393    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9394
 9395    let server_restarts = Arc::new(AtomicUsize::new(0));
 9396    let closure_restarts = Arc::clone(&server_restarts);
 9397    let language_server_name = "test language server";
 9398    let language_name: Arc<str> = "Rust".into();
 9399
 9400    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9401    language_registry.add(Arc::new(Language::new(
 9402        LanguageConfig {
 9403            name: Arc::clone(&language_name),
 9404            matcher: LanguageMatcher {
 9405                path_suffixes: vec!["rs".to_string()],
 9406                ..Default::default()
 9407            },
 9408            ..Default::default()
 9409        },
 9410        Some(tree_sitter_rust::language()),
 9411    )));
 9412    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9413        "Rust",
 9414        FakeLspAdapter {
 9415            name: language_server_name,
 9416            initialization_options: Some(json!({
 9417                "testOptionValue": true
 9418            })),
 9419            initializer: Some(Box::new(move |fake_server| {
 9420                let task_restarts = Arc::clone(&closure_restarts);
 9421                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9422                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9423                    futures::future::ready(Ok(()))
 9424                });
 9425            })),
 9426            ..Default::default()
 9427        },
 9428    );
 9429
 9430    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9431    let _buffer = project
 9432        .update(cx, |project, cx| {
 9433            project.open_local_buffer("/a/main.rs", cx)
 9434        })
 9435        .await
 9436        .unwrap();
 9437    let _fake_server = fake_servers.next().await.unwrap();
 9438    update_test_language_settings(cx, |language_settings| {
 9439        language_settings.languages.insert(
 9440            Arc::clone(&language_name),
 9441            LanguageSettingsContent {
 9442                tab_size: NonZeroU32::new(8),
 9443                ..Default::default()
 9444            },
 9445        );
 9446    });
 9447    cx.executor().run_until_parked();
 9448    assert_eq!(
 9449        server_restarts.load(atomic::Ordering::Acquire),
 9450        0,
 9451        "Should not restart LSP server on an unrelated change"
 9452    );
 9453
 9454    update_test_project_settings(cx, |project_settings| {
 9455        project_settings.lsp.insert(
 9456            "Some other server name".into(),
 9457            LspSettings {
 9458                binary: None,
 9459                settings: None,
 9460                initialization_options: Some(json!({
 9461                    "some other init value": false
 9462                })),
 9463            },
 9464        );
 9465    });
 9466    cx.executor().run_until_parked();
 9467    assert_eq!(
 9468        server_restarts.load(atomic::Ordering::Acquire),
 9469        0,
 9470        "Should not restart LSP server on an unrelated LSP settings change"
 9471    );
 9472
 9473    update_test_project_settings(cx, |project_settings| {
 9474        project_settings.lsp.insert(
 9475            language_server_name.into(),
 9476            LspSettings {
 9477                binary: None,
 9478                settings: None,
 9479                initialization_options: Some(json!({
 9480                    "anotherInitValue": false
 9481                })),
 9482            },
 9483        );
 9484    });
 9485    cx.executor().run_until_parked();
 9486    assert_eq!(
 9487        server_restarts.load(atomic::Ordering::Acquire),
 9488        1,
 9489        "Should restart LSP server on a related LSP settings change"
 9490    );
 9491
 9492    update_test_project_settings(cx, |project_settings| {
 9493        project_settings.lsp.insert(
 9494            language_server_name.into(),
 9495            LspSettings {
 9496                binary: None,
 9497                settings: None,
 9498                initialization_options: Some(json!({
 9499                    "anotherInitValue": false
 9500                })),
 9501            },
 9502        );
 9503    });
 9504    cx.executor().run_until_parked();
 9505    assert_eq!(
 9506        server_restarts.load(atomic::Ordering::Acquire),
 9507        1,
 9508        "Should not restart LSP server on a related LSP settings change that is the same"
 9509    );
 9510
 9511    update_test_project_settings(cx, |project_settings| {
 9512        project_settings.lsp.insert(
 9513            language_server_name.into(),
 9514            LspSettings {
 9515                binary: None,
 9516                settings: None,
 9517                initialization_options: None,
 9518            },
 9519        );
 9520    });
 9521    cx.executor().run_until_parked();
 9522    assert_eq!(
 9523        server_restarts.load(atomic::Ordering::Acquire),
 9524        2,
 9525        "Should restart LSP server on another related LSP settings change"
 9526    );
 9527}
 9528
 9529#[gpui::test]
 9530async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9531    init_test(cx, |_| {});
 9532
 9533    let mut cx = EditorLspTestContext::new_rust(
 9534        lsp::ServerCapabilities {
 9535            completion_provider: Some(lsp::CompletionOptions {
 9536                trigger_characters: Some(vec![".".to_string()]),
 9537                resolve_provider: Some(true),
 9538                ..Default::default()
 9539            }),
 9540            ..Default::default()
 9541        },
 9542        cx,
 9543    )
 9544    .await;
 9545
 9546    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9547    cx.simulate_keystroke(".");
 9548    let completion_item = lsp::CompletionItem {
 9549        label: "some".into(),
 9550        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9551        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9552        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9553            kind: lsp::MarkupKind::Markdown,
 9554            value: "```rust\nSome(2)\n```".to_string(),
 9555        })),
 9556        deprecated: Some(false),
 9557        sort_text: Some("fffffff2".to_string()),
 9558        filter_text: Some("some".to_string()),
 9559        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9560        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9561            range: lsp::Range {
 9562                start: lsp::Position {
 9563                    line: 0,
 9564                    character: 22,
 9565                },
 9566                end: lsp::Position {
 9567                    line: 0,
 9568                    character: 22,
 9569                },
 9570            },
 9571            new_text: "Some(2)".to_string(),
 9572        })),
 9573        additional_text_edits: Some(vec![lsp::TextEdit {
 9574            range: lsp::Range {
 9575                start: lsp::Position {
 9576                    line: 0,
 9577                    character: 20,
 9578                },
 9579                end: lsp::Position {
 9580                    line: 0,
 9581                    character: 22,
 9582                },
 9583            },
 9584            new_text: "".to_string(),
 9585        }]),
 9586        ..Default::default()
 9587    };
 9588
 9589    let closure_completion_item = completion_item.clone();
 9590    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9591        let task_completion_item = closure_completion_item.clone();
 9592        async move {
 9593            Ok(Some(lsp::CompletionResponse::Array(vec![
 9594                task_completion_item,
 9595            ])))
 9596        }
 9597    });
 9598
 9599    request.next().await;
 9600
 9601    cx.condition(|editor, _| editor.context_menu_visible())
 9602        .await;
 9603    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9604        editor
 9605            .confirm_completion(&ConfirmCompletion::default(), cx)
 9606            .unwrap()
 9607    });
 9608    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9609
 9610    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9611        let task_completion_item = completion_item.clone();
 9612        async move { Ok(task_completion_item) }
 9613    })
 9614    .next()
 9615    .await
 9616    .unwrap();
 9617    apply_additional_edits.await.unwrap();
 9618    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9619}
 9620
 9621#[gpui::test]
 9622async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9623    init_test(cx, |_| {});
 9624
 9625    let mut cx = EditorLspTestContext::new(
 9626        Language::new(
 9627            LanguageConfig {
 9628                matcher: LanguageMatcher {
 9629                    path_suffixes: vec!["jsx".into()],
 9630                    ..Default::default()
 9631                },
 9632                overrides: [(
 9633                    "element".into(),
 9634                    LanguageConfigOverride {
 9635                        word_characters: Override::Set(['-'].into_iter().collect()),
 9636                        ..Default::default()
 9637                    },
 9638                )]
 9639                .into_iter()
 9640                .collect(),
 9641                ..Default::default()
 9642            },
 9643            Some(tree_sitter_typescript::language_tsx()),
 9644        )
 9645        .with_override_query("(jsx_self_closing_element) @element")
 9646        .unwrap(),
 9647        lsp::ServerCapabilities {
 9648            completion_provider: Some(lsp::CompletionOptions {
 9649                trigger_characters: Some(vec![":".to_string()]),
 9650                ..Default::default()
 9651            }),
 9652            ..Default::default()
 9653        },
 9654        cx,
 9655    )
 9656    .await;
 9657
 9658    cx.lsp
 9659        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9660            Ok(Some(lsp::CompletionResponse::Array(vec![
 9661                lsp::CompletionItem {
 9662                    label: "bg-blue".into(),
 9663                    ..Default::default()
 9664                },
 9665                lsp::CompletionItem {
 9666                    label: "bg-red".into(),
 9667                    ..Default::default()
 9668                },
 9669                lsp::CompletionItem {
 9670                    label: "bg-yellow".into(),
 9671                    ..Default::default()
 9672                },
 9673            ])))
 9674        });
 9675
 9676    cx.set_state(r#"<p class="bgˇ" />"#);
 9677
 9678    // Trigger completion when typing a dash, because the dash is an extra
 9679    // word character in the 'element' scope, which contains the cursor.
 9680    cx.simulate_keystroke("-");
 9681    cx.executor().run_until_parked();
 9682    cx.update_editor(|editor, _| {
 9683        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9684            assert_eq!(
 9685                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9686                &["bg-red", "bg-blue", "bg-yellow"]
 9687            );
 9688        } else {
 9689            panic!("expected completion menu to be open");
 9690        }
 9691    });
 9692
 9693    cx.simulate_keystroke("l");
 9694    cx.executor().run_until_parked();
 9695    cx.update_editor(|editor, _| {
 9696        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9697            assert_eq!(
 9698                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9699                &["bg-blue", "bg-yellow"]
 9700            );
 9701        } else {
 9702            panic!("expected completion menu to be open");
 9703        }
 9704    });
 9705
 9706    // When filtering completions, consider the character after the '-' to
 9707    // be the start of a subword.
 9708    cx.set_state(r#"<p class="yelˇ" />"#);
 9709    cx.simulate_keystroke("l");
 9710    cx.executor().run_until_parked();
 9711    cx.update_editor(|editor, _| {
 9712        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9713            assert_eq!(
 9714                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9715                &["bg-yellow"]
 9716            );
 9717        } else {
 9718            panic!("expected completion menu to be open");
 9719        }
 9720    });
 9721}
 9722
 9723#[gpui::test]
 9724async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9725    init_test(cx, |settings| {
 9726        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 9727    });
 9728
 9729    let fs = FakeFs::new(cx.executor());
 9730    fs.insert_file("/file.ts", Default::default()).await;
 9731
 9732    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9733    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9734
 9735    language_registry.add(Arc::new(Language::new(
 9736        LanguageConfig {
 9737            name: "TypeScript".into(),
 9738            matcher: LanguageMatcher {
 9739                path_suffixes: vec!["ts".to_string()],
 9740                ..Default::default()
 9741            },
 9742            ..Default::default()
 9743        },
 9744        Some(tree_sitter_rust::language()),
 9745    )));
 9746    update_test_language_settings(cx, |settings| {
 9747        settings.defaults.prettier = Some(PrettierSettings {
 9748            allowed: true,
 9749            ..PrettierSettings::default()
 9750        });
 9751    });
 9752
 9753    let test_plugin = "test_plugin";
 9754    let _ = language_registry.register_fake_lsp_adapter(
 9755        "TypeScript",
 9756        FakeLspAdapter {
 9757            prettier_plugins: vec![test_plugin],
 9758            ..Default::default()
 9759        },
 9760    );
 9761
 9762    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9763    let buffer = project
 9764        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9765        .await
 9766        .unwrap();
 9767
 9768    let buffer_text = "one\ntwo\nthree\n";
 9769    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9770    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9771    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9772
 9773    editor
 9774        .update(cx, |editor, cx| {
 9775            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9776        })
 9777        .unwrap()
 9778        .await;
 9779    assert_eq!(
 9780        editor.update(cx, |editor, cx| editor.text(cx)),
 9781        buffer_text.to_string() + prettier_format_suffix,
 9782        "Test prettier formatting was not applied to the original buffer text",
 9783    );
 9784
 9785    update_test_language_settings(cx, |settings| {
 9786        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 9787    });
 9788    let format = editor.update(cx, |editor, cx| {
 9789        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9790    });
 9791    format.await.unwrap();
 9792    assert_eq!(
 9793        editor.update(cx, |editor, cx| editor.text(cx)),
 9794        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9795        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9796    );
 9797}
 9798
 9799#[gpui::test]
 9800async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9801    init_test(cx, |_| {});
 9802    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9803    let base_text = indoc! {r#"struct Row;
 9804struct Row1;
 9805struct Row2;
 9806
 9807struct Row4;
 9808struct Row5;
 9809struct Row6;
 9810
 9811struct Row8;
 9812struct Row9;
 9813struct Row10;"#};
 9814
 9815    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9816    assert_hunk_revert(
 9817        indoc! {r#"struct Row;
 9818                   struct Row1;
 9819                   struct Row1.1;
 9820                   struct Row1.2;
 9821                   struct Row2;ˇ
 9822
 9823                   struct Row4;
 9824                   struct Row5;
 9825                   struct Row6;
 9826
 9827                   struct Row8;
 9828                   ˇstruct Row9;
 9829                   struct Row9.1;
 9830                   struct Row9.2;
 9831                   struct Row9.3;
 9832                   struct Row10;"#},
 9833        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9834        indoc! {r#"struct Row;
 9835                   struct Row1;
 9836                   struct Row1.1;
 9837                   struct Row1.2;
 9838                   struct Row2;ˇ
 9839
 9840                   struct Row4;
 9841                   struct Row5;
 9842                   struct Row6;
 9843
 9844                   struct Row8;
 9845                   ˇstruct Row9;
 9846                   struct Row9.1;
 9847                   struct Row9.2;
 9848                   struct Row9.3;
 9849                   struct Row10;"#},
 9850        base_text,
 9851        &mut cx,
 9852    );
 9853    // Same for selections
 9854    assert_hunk_revert(
 9855        indoc! {r#"struct Row;
 9856                   struct Row1;
 9857                   struct Row2;
 9858                   struct Row2.1;
 9859                   struct Row2.2;
 9860                   «ˇ
 9861                   struct Row4;
 9862                   struct» Row5;
 9863                   «struct Row6;
 9864                   ˇ»
 9865                   struct Row9.1;
 9866                   struct Row9.2;
 9867                   struct Row9.3;
 9868                   struct Row8;
 9869                   struct Row9;
 9870                   struct Row10;"#},
 9871        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9872        indoc! {r#"struct Row;
 9873                   struct Row1;
 9874                   struct Row2;
 9875                   struct Row2.1;
 9876                   struct Row2.2;
 9877                   «ˇ
 9878                   struct Row4;
 9879                   struct» Row5;
 9880                   «struct Row6;
 9881                   ˇ»
 9882                   struct Row9.1;
 9883                   struct Row9.2;
 9884                   struct Row9.3;
 9885                   struct Row8;
 9886                   struct Row9;
 9887                   struct Row10;"#},
 9888        base_text,
 9889        &mut cx,
 9890    );
 9891
 9892    // When carets and selections intersect the addition hunks, those are reverted.
 9893    // Adjacent carets got merged.
 9894    assert_hunk_revert(
 9895        indoc! {r#"struct Row;
 9896                   ˇ// something on the top
 9897                   struct Row1;
 9898                   struct Row2;
 9899                   struct Roˇw3.1;
 9900                   struct Row2.2;
 9901                   struct Row2.3;ˇ
 9902
 9903                   struct Row4;
 9904                   struct ˇRow5.1;
 9905                   struct Row5.2;
 9906                   struct «Rowˇ»5.3;
 9907                   struct Row5;
 9908                   struct Row6;
 9909                   ˇ
 9910                   struct Row9.1;
 9911                   struct «Rowˇ»9.2;
 9912                   struct «ˇRow»9.3;
 9913                   struct Row8;
 9914                   struct Row9;
 9915                   «ˇ// something on bottom»
 9916                   struct Row10;"#},
 9917        vec![
 9918            DiffHunkStatus::Added,
 9919            DiffHunkStatus::Added,
 9920            DiffHunkStatus::Added,
 9921            DiffHunkStatus::Added,
 9922            DiffHunkStatus::Added,
 9923        ],
 9924        indoc! {r#"struct Row;
 9925                   ˇstruct Row1;
 9926                   struct Row2;
 9927                   ˇ
 9928                   struct Row4;
 9929                   ˇstruct Row5;
 9930                   struct Row6;
 9931                   ˇ
 9932                   ˇstruct Row8;
 9933                   struct Row9;
 9934                   ˇstruct Row10;"#},
 9935        base_text,
 9936        &mut cx,
 9937    );
 9938}
 9939
 9940#[gpui::test]
 9941async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9942    init_test(cx, |_| {});
 9943    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9944    let base_text = indoc! {r#"struct Row;
 9945struct Row1;
 9946struct Row2;
 9947
 9948struct Row4;
 9949struct Row5;
 9950struct Row6;
 9951
 9952struct Row8;
 9953struct Row9;
 9954struct Row10;"#};
 9955
 9956    // Modification hunks behave the same as the addition ones.
 9957    assert_hunk_revert(
 9958        indoc! {r#"struct Row;
 9959                   struct Row1;
 9960                   struct Row33;
 9961                   ˇ
 9962                   struct Row4;
 9963                   struct Row5;
 9964                   struct Row6;
 9965                   ˇ
 9966                   struct Row99;
 9967                   struct Row9;
 9968                   struct Row10;"#},
 9969        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9970        indoc! {r#"struct Row;
 9971                   struct Row1;
 9972                   struct Row33;
 9973                   ˇ
 9974                   struct Row4;
 9975                   struct Row5;
 9976                   struct Row6;
 9977                   ˇ
 9978                   struct Row99;
 9979                   struct Row9;
 9980                   struct Row10;"#},
 9981        base_text,
 9982        &mut cx,
 9983    );
 9984    assert_hunk_revert(
 9985        indoc! {r#"struct Row;
 9986                   struct Row1;
 9987                   struct Row33;
 9988                   «ˇ
 9989                   struct Row4;
 9990                   struct» Row5;
 9991                   «struct Row6;
 9992                   ˇ»
 9993                   struct Row99;
 9994                   struct Row9;
 9995                   struct Row10;"#},
 9996        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9997        indoc! {r#"struct Row;
 9998                   struct Row1;
 9999                   struct Row33;
10000                   «ˇ
10001                   struct Row4;
10002                   struct» Row5;
10003                   «struct Row6;
10004                   ˇ»
10005                   struct Row99;
10006                   struct Row9;
10007                   struct Row10;"#},
10008        base_text,
10009        &mut cx,
10010    );
10011
10012    assert_hunk_revert(
10013        indoc! {r#"ˇstruct Row1.1;
10014                   struct Row1;
10015                   «ˇstr»uct Row22;
10016
10017                   struct ˇRow44;
10018                   struct Row5;
10019                   struct «Rˇ»ow66;ˇ
10020
10021                   «struˇ»ct Row88;
10022                   struct Row9;
10023                   struct Row1011;ˇ"#},
10024        vec![
10025            DiffHunkStatus::Modified,
10026            DiffHunkStatus::Modified,
10027            DiffHunkStatus::Modified,
10028            DiffHunkStatus::Modified,
10029            DiffHunkStatus::Modified,
10030            DiffHunkStatus::Modified,
10031        ],
10032        indoc! {r#"struct Row;
10033                   ˇstruct Row1;
10034                   struct Row2;
10035                   ˇ
10036                   struct Row4;
10037                   ˇstruct Row5;
10038                   struct Row6;
10039                   ˇ
10040                   struct Row8;
10041                   ˇstruct Row9;
10042                   struct Row10;ˇ"#},
10043        base_text,
10044        &mut cx,
10045    );
10046}
10047
10048#[gpui::test]
10049async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10050    init_test(cx, |_| {});
10051    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10052    let base_text = indoc! {r#"struct Row;
10053struct Row1;
10054struct Row2;
10055
10056struct Row4;
10057struct Row5;
10058struct Row6;
10059
10060struct Row8;
10061struct Row9;
10062struct Row10;"#};
10063
10064    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
10065    assert_hunk_revert(
10066        indoc! {r#"struct Row;
10067                   struct Row2;
10068
10069                   ˇstruct Row4;
10070                   struct Row5;
10071                   struct Row6;
10072                   ˇ
10073                   struct Row8;
10074                   struct Row10;"#},
10075        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10076        indoc! {r#"struct Row;
10077                   struct Row2;
10078
10079                   ˇstruct Row4;
10080                   struct Row5;
10081                   struct Row6;
10082                   ˇ
10083                   struct Row8;
10084                   struct Row10;"#},
10085        base_text,
10086        &mut cx,
10087    );
10088    assert_hunk_revert(
10089        indoc! {r#"struct Row;
10090                   struct Row2;
10091
10092                   «ˇstruct Row4;
10093                   struct» Row5;
10094                   «struct Row6;
10095                   ˇ»
10096                   struct Row8;
10097                   struct Row10;"#},
10098        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10099        indoc! {r#"struct Row;
10100                   struct Row2;
10101
10102                   «ˇstruct Row4;
10103                   struct» Row5;
10104                   «struct Row6;
10105                   ˇ»
10106                   struct Row8;
10107                   struct Row10;"#},
10108        base_text,
10109        &mut cx,
10110    );
10111
10112    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10113    assert_hunk_revert(
10114        indoc! {r#"struct Row;
10115                   ˇstruct Row2;
10116
10117                   struct Row4;
10118                   struct Row5;
10119                   struct Row6;
10120
10121                   struct Row8;ˇ
10122                   struct Row10;"#},
10123        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10124        indoc! {r#"struct Row;
10125                   struct Row1;
10126                   ˇstruct Row2;
10127
10128                   struct Row4;
10129                   struct Row5;
10130                   struct Row6;
10131
10132                   struct Row8;ˇ
10133                   struct Row9;
10134                   struct Row10;"#},
10135        base_text,
10136        &mut cx,
10137    );
10138    assert_hunk_revert(
10139        indoc! {r#"struct Row;
10140                   struct Row2«ˇ;
10141                   struct Row4;
10142                   struct» Row5;
10143                   «struct Row6;
10144
10145                   struct Row8;ˇ»
10146                   struct Row10;"#},
10147        vec![
10148            DiffHunkStatus::Removed,
10149            DiffHunkStatus::Removed,
10150            DiffHunkStatus::Removed,
10151        ],
10152        indoc! {r#"struct Row;
10153                   struct Row1;
10154                   struct Row2«ˇ;
10155
10156                   struct Row4;
10157                   struct» Row5;
10158                   «struct Row6;
10159
10160                   struct Row8;ˇ»
10161                   struct Row9;
10162                   struct Row10;"#},
10163        base_text,
10164        &mut cx,
10165    );
10166}
10167
10168#[gpui::test]
10169async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10170    init_test(cx, |_| {});
10171
10172    let cols = 4;
10173    let rows = 10;
10174    let sample_text_1 = sample_text(rows, cols, 'a');
10175    assert_eq!(
10176        sample_text_1,
10177        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10178    );
10179    let sample_text_2 = sample_text(rows, cols, 'l');
10180    assert_eq!(
10181        sample_text_2,
10182        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10183    );
10184    let sample_text_3 = sample_text(rows, cols, 'v');
10185    assert_eq!(
10186        sample_text_3,
10187        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10188    );
10189
10190    fn diff_every_buffer_row(
10191        buffer: &Model<Buffer>,
10192        sample_text: String,
10193        cols: usize,
10194        cx: &mut gpui::TestAppContext,
10195    ) {
10196        // revert first character in each row, creating one large diff hunk per buffer
10197        let is_first_char = |offset: usize| offset % cols == 0;
10198        buffer.update(cx, |buffer, cx| {
10199            buffer.set_text(
10200                sample_text
10201                    .chars()
10202                    .enumerate()
10203                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10204                    .collect::<String>(),
10205                cx,
10206            );
10207            buffer.set_diff_base(Some(sample_text), cx);
10208        });
10209        cx.executor().run_until_parked();
10210    }
10211
10212    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10213    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10214
10215    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10216    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10217
10218    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10219    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10220
10221    let multibuffer = cx.new_model(|cx| {
10222        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10223        multibuffer.push_excerpts(
10224            buffer_1.clone(),
10225            [
10226                ExcerptRange {
10227                    context: Point::new(0, 0)..Point::new(3, 0),
10228                    primary: None,
10229                },
10230                ExcerptRange {
10231                    context: Point::new(5, 0)..Point::new(7, 0),
10232                    primary: None,
10233                },
10234                ExcerptRange {
10235                    context: Point::new(9, 0)..Point::new(10, 4),
10236                    primary: None,
10237                },
10238            ],
10239            cx,
10240        );
10241        multibuffer.push_excerpts(
10242            buffer_2.clone(),
10243            [
10244                ExcerptRange {
10245                    context: Point::new(0, 0)..Point::new(3, 0),
10246                    primary: None,
10247                },
10248                ExcerptRange {
10249                    context: Point::new(5, 0)..Point::new(7, 0),
10250                    primary: None,
10251                },
10252                ExcerptRange {
10253                    context: Point::new(9, 0)..Point::new(10, 4),
10254                    primary: None,
10255                },
10256            ],
10257            cx,
10258        );
10259        multibuffer.push_excerpts(
10260            buffer_3.clone(),
10261            [
10262                ExcerptRange {
10263                    context: Point::new(0, 0)..Point::new(3, 0),
10264                    primary: None,
10265                },
10266                ExcerptRange {
10267                    context: Point::new(5, 0)..Point::new(7, 0),
10268                    primary: None,
10269                },
10270                ExcerptRange {
10271                    context: Point::new(9, 0)..Point::new(10, 4),
10272                    primary: None,
10273                },
10274            ],
10275            cx,
10276        );
10277        multibuffer
10278    });
10279
10280    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10281    editor.update(cx, |editor, cx| {
10282        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");
10283        editor.select_all(&SelectAll, cx);
10284        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10285    });
10286    cx.executor().run_until_parked();
10287    // When all ranges are selected, all buffer hunks are reverted.
10288    editor.update(cx, |editor, cx| {
10289        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");
10290    });
10291    buffer_1.update(cx, |buffer, _| {
10292        assert_eq!(buffer.text(), sample_text_1);
10293    });
10294    buffer_2.update(cx, |buffer, _| {
10295        assert_eq!(buffer.text(), sample_text_2);
10296    });
10297    buffer_3.update(cx, |buffer, _| {
10298        assert_eq!(buffer.text(), sample_text_3);
10299    });
10300
10301    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10302    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10303    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10304    editor.update(cx, |editor, cx| {
10305        editor.change_selections(None, cx, |s| {
10306            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10307        });
10308        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10309    });
10310    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10311    // but not affect buffer_2 and its related excerpts.
10312    editor.update(cx, |editor, cx| {
10313        assert_eq!(
10314            editor.text(cx),
10315            "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"
10316        );
10317    });
10318    buffer_1.update(cx, |buffer, _| {
10319        assert_eq!(buffer.text(), sample_text_1);
10320    });
10321    buffer_2.update(cx, |buffer, _| {
10322        assert_eq!(
10323            buffer.text(),
10324            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10325        );
10326    });
10327    buffer_3.update(cx, |buffer, _| {
10328        assert_eq!(
10329            buffer.text(),
10330            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10331        );
10332    });
10333}
10334
10335#[gpui::test]
10336async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10337    init_test(cx, |_| {});
10338
10339    let cols = 4;
10340    let rows = 10;
10341    let sample_text_1 = sample_text(rows, cols, 'a');
10342    assert_eq!(
10343        sample_text_1,
10344        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10345    );
10346    let sample_text_2 = sample_text(rows, cols, 'l');
10347    assert_eq!(
10348        sample_text_2,
10349        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10350    );
10351    let sample_text_3 = sample_text(rows, cols, 'v');
10352    assert_eq!(
10353        sample_text_3,
10354        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10355    );
10356
10357    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10358    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10359    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10360
10361    let multi_buffer = cx.new_model(|cx| {
10362        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10363        multibuffer.push_excerpts(
10364            buffer_1.clone(),
10365            [
10366                ExcerptRange {
10367                    context: Point::new(0, 0)..Point::new(3, 0),
10368                    primary: None,
10369                },
10370                ExcerptRange {
10371                    context: Point::new(5, 0)..Point::new(7, 0),
10372                    primary: None,
10373                },
10374                ExcerptRange {
10375                    context: Point::new(9, 0)..Point::new(10, 4),
10376                    primary: None,
10377                },
10378            ],
10379            cx,
10380        );
10381        multibuffer.push_excerpts(
10382            buffer_2.clone(),
10383            [
10384                ExcerptRange {
10385                    context: Point::new(0, 0)..Point::new(3, 0),
10386                    primary: None,
10387                },
10388                ExcerptRange {
10389                    context: Point::new(5, 0)..Point::new(7, 0),
10390                    primary: None,
10391                },
10392                ExcerptRange {
10393                    context: Point::new(9, 0)..Point::new(10, 4),
10394                    primary: None,
10395                },
10396            ],
10397            cx,
10398        );
10399        multibuffer.push_excerpts(
10400            buffer_3.clone(),
10401            [
10402                ExcerptRange {
10403                    context: Point::new(0, 0)..Point::new(3, 0),
10404                    primary: None,
10405                },
10406                ExcerptRange {
10407                    context: Point::new(5, 0)..Point::new(7, 0),
10408                    primary: None,
10409                },
10410                ExcerptRange {
10411                    context: Point::new(9, 0)..Point::new(10, 4),
10412                    primary: None,
10413                },
10414            ],
10415            cx,
10416        );
10417        multibuffer
10418    });
10419
10420    let fs = FakeFs::new(cx.executor());
10421    fs.insert_tree(
10422        "/a",
10423        json!({
10424            "main.rs": sample_text_1,
10425            "other.rs": sample_text_2,
10426            "lib.rs": sample_text_3,
10427        }),
10428    )
10429    .await;
10430    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10431    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10432    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10433    let multi_buffer_editor = cx.new_view(|cx| {
10434        Editor::new(
10435            EditorMode::Full,
10436            multi_buffer,
10437            Some(project.clone()),
10438            true,
10439            cx,
10440        )
10441    });
10442    let multibuffer_item_id = workspace
10443        .update(cx, |workspace, cx| {
10444            assert!(
10445                workspace.active_item(cx).is_none(),
10446                "active item should be None before the first item is added"
10447            );
10448            workspace.add_item_to_active_pane(
10449                Box::new(multi_buffer_editor.clone()),
10450                None,
10451                true,
10452                cx,
10453            );
10454            let active_item = workspace
10455                .active_item(cx)
10456                .expect("should have an active item after adding the multi buffer");
10457            assert!(
10458                !active_item.is_singleton(cx),
10459                "A multi buffer was expected to active after adding"
10460            );
10461            active_item.item_id()
10462        })
10463        .unwrap();
10464    cx.executor().run_until_parked();
10465
10466    multi_buffer_editor.update(cx, |editor, cx| {
10467        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10468        editor.open_excerpts(&OpenExcerpts, cx);
10469    });
10470    cx.executor().run_until_parked();
10471    let first_item_id = workspace
10472        .update(cx, |workspace, cx| {
10473            let active_item = workspace
10474                .active_item(cx)
10475                .expect("should have an active item after navigating into the 1st buffer");
10476            let first_item_id = active_item.item_id();
10477            assert_ne!(
10478                first_item_id, multibuffer_item_id,
10479                "Should navigate into the 1st buffer and activate it"
10480            );
10481            assert!(
10482                active_item.is_singleton(cx),
10483                "New active item should be a singleton buffer"
10484            );
10485            assert_eq!(
10486                active_item
10487                    .act_as::<Editor>(cx)
10488                    .expect("should have navigated into an editor for the 1st buffer")
10489                    .read(cx)
10490                    .text(cx),
10491                sample_text_1
10492            );
10493
10494            workspace
10495                .go_back(workspace.active_pane().downgrade(), cx)
10496                .detach_and_log_err(cx);
10497
10498            first_item_id
10499        })
10500        .unwrap();
10501    cx.executor().run_until_parked();
10502    workspace
10503        .update(cx, |workspace, cx| {
10504            let active_item = workspace
10505                .active_item(cx)
10506                .expect("should have an active item after navigating back");
10507            assert_eq!(
10508                active_item.item_id(),
10509                multibuffer_item_id,
10510                "Should navigate back to the multi buffer"
10511            );
10512            assert!(!active_item.is_singleton(cx));
10513        })
10514        .unwrap();
10515
10516    multi_buffer_editor.update(cx, |editor, cx| {
10517        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10518            s.select_ranges(Some(39..40))
10519        });
10520        editor.open_excerpts(&OpenExcerpts, cx);
10521    });
10522    cx.executor().run_until_parked();
10523    let second_item_id = workspace
10524        .update(cx, |workspace, cx| {
10525            let active_item = workspace
10526                .active_item(cx)
10527                .expect("should have an active item after navigating into the 2nd buffer");
10528            let second_item_id = active_item.item_id();
10529            assert_ne!(
10530                second_item_id, multibuffer_item_id,
10531                "Should navigate away from the multibuffer"
10532            );
10533            assert_ne!(
10534                second_item_id, first_item_id,
10535                "Should navigate into the 2nd buffer and activate it"
10536            );
10537            assert!(
10538                active_item.is_singleton(cx),
10539                "New active item should be a singleton buffer"
10540            );
10541            assert_eq!(
10542                active_item
10543                    .act_as::<Editor>(cx)
10544                    .expect("should have navigated into an editor")
10545                    .read(cx)
10546                    .text(cx),
10547                sample_text_2
10548            );
10549
10550            workspace
10551                .go_back(workspace.active_pane().downgrade(), cx)
10552                .detach_and_log_err(cx);
10553
10554            second_item_id
10555        })
10556        .unwrap();
10557    cx.executor().run_until_parked();
10558    workspace
10559        .update(cx, |workspace, cx| {
10560            let active_item = workspace
10561                .active_item(cx)
10562                .expect("should have an active item after navigating back from the 2nd buffer");
10563            assert_eq!(
10564                active_item.item_id(),
10565                multibuffer_item_id,
10566                "Should navigate back from the 2nd buffer to the multi buffer"
10567            );
10568            assert!(!active_item.is_singleton(cx));
10569        })
10570        .unwrap();
10571
10572    multi_buffer_editor.update(cx, |editor, cx| {
10573        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10574            s.select_ranges(Some(60..70))
10575        });
10576        editor.open_excerpts(&OpenExcerpts, cx);
10577    });
10578    cx.executor().run_until_parked();
10579    workspace
10580        .update(cx, |workspace, cx| {
10581            let active_item = workspace
10582                .active_item(cx)
10583                .expect("should have an active item after navigating into the 3rd buffer");
10584            let third_item_id = active_item.item_id();
10585            assert_ne!(
10586                third_item_id, multibuffer_item_id,
10587                "Should navigate into the 3rd buffer and activate it"
10588            );
10589            assert_ne!(third_item_id, first_item_id);
10590            assert_ne!(third_item_id, second_item_id);
10591            assert!(
10592                active_item.is_singleton(cx),
10593                "New active item should be a singleton buffer"
10594            );
10595            assert_eq!(
10596                active_item
10597                    .act_as::<Editor>(cx)
10598                    .expect("should have navigated into an editor")
10599                    .read(cx)
10600                    .text(cx),
10601                sample_text_3
10602            );
10603
10604            workspace
10605                .go_back(workspace.active_pane().downgrade(), cx)
10606                .detach_and_log_err(cx);
10607        })
10608        .unwrap();
10609    cx.executor().run_until_parked();
10610    workspace
10611        .update(cx, |workspace, cx| {
10612            let active_item = workspace
10613                .active_item(cx)
10614                .expect("should have an active item after navigating back from the 3rd buffer");
10615            assert_eq!(
10616                active_item.item_id(),
10617                multibuffer_item_id,
10618                "Should navigate back from the 3rd buffer to the multi buffer"
10619            );
10620            assert!(!active_item.is_singleton(cx));
10621        })
10622        .unwrap();
10623}
10624
10625#[gpui::test]
10626async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10627    init_test(cx, |_| {});
10628
10629    let mut cx = EditorTestContext::new(cx).await;
10630
10631    let diff_base = r#"
10632        use some::mod;
10633
10634        const A: u32 = 42;
10635
10636        fn main() {
10637            println!("hello");
10638
10639            println!("world");
10640        }
10641        "#
10642    .unindent();
10643
10644    cx.set_state(
10645        &r#"
10646        use some::modified;
10647
10648        ˇ
10649        fn main() {
10650            println!("hello there");
10651
10652            println!("around the");
10653            println!("world");
10654        }
10655        "#
10656        .unindent(),
10657    );
10658
10659    cx.set_diff_base(Some(&diff_base));
10660    executor.run_until_parked();
10661    let unexpanded_hunks = vec![
10662        (
10663            "use some::mod;\n".to_string(),
10664            DiffHunkStatus::Modified,
10665            DisplayRow(0)..DisplayRow(1),
10666        ),
10667        (
10668            "const A: u32 = 42;\n".to_string(),
10669            DiffHunkStatus::Removed,
10670            DisplayRow(2)..DisplayRow(2),
10671        ),
10672        (
10673            "    println!(\"hello\");\n".to_string(),
10674            DiffHunkStatus::Modified,
10675            DisplayRow(4)..DisplayRow(5),
10676        ),
10677        (
10678            "".to_string(),
10679            DiffHunkStatus::Added,
10680            DisplayRow(6)..DisplayRow(7),
10681        ),
10682    ];
10683    cx.update_editor(|editor, cx| {
10684        let snapshot = editor.snapshot(cx);
10685        let all_hunks = editor_hunks(editor, &snapshot, cx);
10686        assert_eq!(all_hunks, unexpanded_hunks);
10687    });
10688
10689    cx.update_editor(|editor, cx| {
10690        for _ in 0..4 {
10691            editor.go_to_hunk(&GoToHunk, cx);
10692            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10693        }
10694    });
10695    executor.run_until_parked();
10696    cx.assert_editor_state(
10697        &r#"
10698        use some::modified;
10699
10700        ˇ
10701        fn main() {
10702            println!("hello there");
10703
10704            println!("around the");
10705            println!("world");
10706        }
10707        "#
10708        .unindent(),
10709    );
10710    cx.update_editor(|editor, cx| {
10711        let snapshot = editor.snapshot(cx);
10712        let all_hunks = editor_hunks(editor, &snapshot, cx);
10713        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10714        assert_eq!(
10715            expanded_hunks_background_highlights(editor, cx),
10716            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10717            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10718        );
10719        assert_eq!(
10720            all_hunks,
10721            vec![
10722                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10723                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10724                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10725                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10726            ],
10727            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10728            (from modified and removed hunks)"
10729        );
10730        assert_eq!(
10731            all_hunks, all_expanded_hunks,
10732            "Editor hunks should not change and all be expanded"
10733        );
10734    });
10735
10736    cx.update_editor(|editor, cx| {
10737        editor.cancel(&Cancel, cx);
10738
10739        let snapshot = editor.snapshot(cx);
10740        let all_hunks = editor_hunks(editor, &snapshot, cx);
10741        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10742        assert_eq!(
10743            expanded_hunks_background_highlights(editor, cx),
10744            Vec::new(),
10745            "After cancelling in editor, no git highlights should be left"
10746        );
10747        assert_eq!(
10748            all_expanded_hunks,
10749            Vec::new(),
10750            "After cancelling in editor, no hunks should be expanded"
10751        );
10752        assert_eq!(
10753            all_hunks, unexpanded_hunks,
10754            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10755        );
10756    });
10757}
10758
10759#[gpui::test]
10760async fn test_toggled_diff_base_change(
10761    executor: BackgroundExecutor,
10762    cx: &mut gpui::TestAppContext,
10763) {
10764    init_test(cx, |_| {});
10765
10766    let mut cx = EditorTestContext::new(cx).await;
10767
10768    let diff_base = r#"
10769        use some::mod1;
10770        use some::mod2;
10771
10772        const A: u32 = 42;
10773        const B: u32 = 42;
10774        const C: u32 = 42;
10775
10776        fn main(ˇ) {
10777            println!("hello");
10778
10779            println!("world");
10780        }
10781        "#
10782    .unindent();
10783
10784    cx.set_state(
10785        &r#"
10786        use some::mod2;
10787
10788        const A: u32 = 42;
10789        const C: u32 = 42;
10790
10791        fn main(ˇ) {
10792            //println!("hello");
10793
10794            println!("world");
10795            //
10796            //
10797        }
10798        "#
10799        .unindent(),
10800    );
10801
10802    cx.set_diff_base(Some(&diff_base));
10803    executor.run_until_parked();
10804    cx.update_editor(|editor, cx| {
10805        let snapshot = editor.snapshot(cx);
10806        let all_hunks = editor_hunks(editor, &snapshot, cx);
10807        assert_eq!(
10808            all_hunks,
10809            vec![
10810                (
10811                    "use some::mod1;\n".to_string(),
10812                    DiffHunkStatus::Removed,
10813                    DisplayRow(0)..DisplayRow(0)
10814                ),
10815                (
10816                    "const B: u32 = 42;\n".to_string(),
10817                    DiffHunkStatus::Removed,
10818                    DisplayRow(3)..DisplayRow(3)
10819                ),
10820                (
10821                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10822                    DiffHunkStatus::Modified,
10823                    DisplayRow(5)..DisplayRow(7)
10824                ),
10825                (
10826                    "".to_string(),
10827                    DiffHunkStatus::Added,
10828                    DisplayRow(9)..DisplayRow(11)
10829                ),
10830            ]
10831        );
10832    });
10833
10834    cx.update_editor(|editor, cx| {
10835        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10836    });
10837    executor.run_until_parked();
10838    cx.assert_editor_state(
10839        &r#"
10840        use some::mod2;
10841
10842        const A: u32 = 42;
10843        const C: u32 = 42;
10844
10845        fn main(ˇ) {
10846            //println!("hello");
10847
10848            println!("world");
10849            //
10850            //
10851        }
10852        "#
10853        .unindent(),
10854    );
10855    cx.update_editor(|editor, cx| {
10856        let snapshot = editor.snapshot(cx);
10857        let all_hunks = editor_hunks(editor, &snapshot, cx);
10858        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10859        assert_eq!(
10860            expanded_hunks_background_highlights(editor, cx),
10861            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10862            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10863        );
10864        assert_eq!(
10865            all_hunks,
10866            vec![
10867                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10868                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10869                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10870                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10871            ],
10872            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10873            (from modified and removed hunks)"
10874        );
10875        assert_eq!(
10876            all_hunks, all_expanded_hunks,
10877            "Editor hunks should not change and all be expanded"
10878        );
10879    });
10880
10881    cx.set_diff_base(Some("new diff base!"));
10882    executor.run_until_parked();
10883
10884    cx.update_editor(|editor, cx| {
10885        let snapshot = editor.snapshot(cx);
10886        let all_hunks = editor_hunks(editor, &snapshot, cx);
10887        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10888        assert_eq!(
10889            expanded_hunks_background_highlights(editor, cx),
10890            Vec::new(),
10891            "After diff base is changed, old git highlights should be removed"
10892        );
10893        assert_eq!(
10894            all_expanded_hunks,
10895            Vec::new(),
10896            "After diff base is changed, old git hunk expansions should be removed"
10897        );
10898        assert_eq!(
10899            all_hunks,
10900            vec![(
10901                "new diff base!".to_string(),
10902                DiffHunkStatus::Modified,
10903                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10904            )],
10905            "After diff base is changed, hunks should update"
10906        );
10907    });
10908}
10909
10910#[gpui::test]
10911async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10912    init_test(cx, |_| {});
10913
10914    let mut cx = EditorTestContext::new(cx).await;
10915
10916    let diff_base = r#"
10917        use some::mod1;
10918        use some::mod2;
10919
10920        const A: u32 = 42;
10921        const B: u32 = 42;
10922        const C: u32 = 42;
10923
10924        fn main(ˇ) {
10925            println!("hello");
10926
10927            println!("world");
10928        }
10929
10930        fn another() {
10931            println!("another");
10932        }
10933
10934        fn another2() {
10935            println!("another2");
10936        }
10937        "#
10938    .unindent();
10939
10940    cx.set_state(
10941        &r#"
10942        «use some::mod2;
10943
10944        const A: u32 = 42;
10945        const C: u32 = 42;
10946
10947        fn main() {
10948            //println!("hello");
10949
10950            println!("world");
10951            //
10952            //ˇ»
10953        }
10954
10955        fn another() {
10956            println!("another");
10957            println!("another");
10958        }
10959
10960            println!("another2");
10961        }
10962        "#
10963        .unindent(),
10964    );
10965
10966    cx.set_diff_base(Some(&diff_base));
10967    executor.run_until_parked();
10968    cx.update_editor(|editor, cx| {
10969        let snapshot = editor.snapshot(cx);
10970        let all_hunks = editor_hunks(editor, &snapshot, cx);
10971        assert_eq!(
10972            all_hunks,
10973            vec![
10974                (
10975                    "use some::mod1;\n".to_string(),
10976                    DiffHunkStatus::Removed,
10977                    DisplayRow(0)..DisplayRow(0)
10978                ),
10979                (
10980                    "const B: u32 = 42;\n".to_string(),
10981                    DiffHunkStatus::Removed,
10982                    DisplayRow(3)..DisplayRow(3)
10983                ),
10984                (
10985                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10986                    DiffHunkStatus::Modified,
10987                    DisplayRow(5)..DisplayRow(7)
10988                ),
10989                (
10990                    "".to_string(),
10991                    DiffHunkStatus::Added,
10992                    DisplayRow(9)..DisplayRow(11)
10993                ),
10994                (
10995                    "".to_string(),
10996                    DiffHunkStatus::Added,
10997                    DisplayRow(15)..DisplayRow(16)
10998                ),
10999                (
11000                    "fn another2() {\n".to_string(),
11001                    DiffHunkStatus::Removed,
11002                    DisplayRow(18)..DisplayRow(18)
11003                ),
11004            ]
11005        );
11006    });
11007
11008    cx.update_editor(|editor, cx| {
11009        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11010    });
11011    executor.run_until_parked();
11012    cx.assert_editor_state(
11013        &r#"
11014        «use some::mod2;
11015
11016        const A: u32 = 42;
11017        const C: u32 = 42;
11018
11019        fn main() {
11020            //println!("hello");
11021
11022            println!("world");
11023            //
11024            //ˇ»
11025        }
11026
11027        fn another() {
11028            println!("another");
11029            println!("another");
11030        }
11031
11032            println!("another2");
11033        }
11034        "#
11035        .unindent(),
11036    );
11037    cx.update_editor(|editor, cx| {
11038        let snapshot = editor.snapshot(cx);
11039        let all_hunks = editor_hunks(editor, &snapshot, cx);
11040        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11041        assert_eq!(
11042            expanded_hunks_background_highlights(editor, cx),
11043            vec![
11044                DisplayRow(9)..=DisplayRow(10),
11045                DisplayRow(13)..=DisplayRow(14),
11046                DisplayRow(19)..=DisplayRow(19)
11047            ]
11048        );
11049        assert_eq!(
11050            all_hunks,
11051            vec![
11052                (
11053                    "use some::mod1;\n".to_string(),
11054                    DiffHunkStatus::Removed,
11055                    DisplayRow(1)..DisplayRow(1)
11056                ),
11057                (
11058                    "const B: u32 = 42;\n".to_string(),
11059                    DiffHunkStatus::Removed,
11060                    DisplayRow(5)..DisplayRow(5)
11061                ),
11062                (
11063                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11064                    DiffHunkStatus::Modified,
11065                    DisplayRow(9)..DisplayRow(11)
11066                ),
11067                (
11068                    "".to_string(),
11069                    DiffHunkStatus::Added,
11070                    DisplayRow(13)..DisplayRow(15)
11071                ),
11072                (
11073                    "".to_string(),
11074                    DiffHunkStatus::Added,
11075                    DisplayRow(19)..DisplayRow(20)
11076                ),
11077                (
11078                    "fn another2() {\n".to_string(),
11079                    DiffHunkStatus::Removed,
11080                    DisplayRow(23)..DisplayRow(23)
11081                ),
11082            ],
11083        );
11084        assert_eq!(all_hunks, all_expanded_hunks);
11085    });
11086
11087    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11088    cx.executor().run_until_parked();
11089    cx.assert_editor_state(
11090        &r#"
11091        «use some::mod2;
11092
11093        const A: u32 = 42;
11094        const C: u32 = 42;
11095
11096        fn main() {
11097            //println!("hello");
11098
11099            println!("world");
11100            //
11101            //ˇ»
11102        }
11103
11104        fn another() {
11105            println!("another");
11106            println!("another");
11107        }
11108
11109            println!("another2");
11110        }
11111        "#
11112        .unindent(),
11113    );
11114    cx.update_editor(|editor, cx| {
11115        let snapshot = editor.snapshot(cx);
11116        let all_hunks = editor_hunks(editor, &snapshot, cx);
11117        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11118        assert_eq!(
11119            expanded_hunks_background_highlights(editor, cx),
11120            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11121            "Only one hunk is left not folded, its highlight should be visible"
11122        );
11123        assert_eq!(
11124            all_hunks,
11125            vec![
11126                (
11127                    "use some::mod1;\n".to_string(),
11128                    DiffHunkStatus::Removed,
11129                    DisplayRow(0)..DisplayRow(0)
11130                ),
11131                (
11132                    "const B: u32 = 42;\n".to_string(),
11133                    DiffHunkStatus::Removed,
11134                    DisplayRow(0)..DisplayRow(0)
11135                ),
11136                (
11137                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11138                    DiffHunkStatus::Modified,
11139                    DisplayRow(0)..DisplayRow(0)
11140                ),
11141                (
11142                    "".to_string(),
11143                    DiffHunkStatus::Added,
11144                    DisplayRow(0)..DisplayRow(1)
11145                ),
11146                (
11147                    "".to_string(),
11148                    DiffHunkStatus::Added,
11149                    DisplayRow(5)..DisplayRow(6)
11150                ),
11151                (
11152                    "fn another2() {\n".to_string(),
11153                    DiffHunkStatus::Removed,
11154                    DisplayRow(9)..DisplayRow(9)
11155                ),
11156            ],
11157            "Hunk list should still return shifted folded hunks"
11158        );
11159        assert_eq!(
11160            all_expanded_hunks,
11161            vec![
11162                (
11163                    "".to_string(),
11164                    DiffHunkStatus::Added,
11165                    DisplayRow(5)..DisplayRow(6)
11166                ),
11167                (
11168                    "fn another2() {\n".to_string(),
11169                    DiffHunkStatus::Removed,
11170                    DisplayRow(9)..DisplayRow(9)
11171                ),
11172            ],
11173            "Only non-folded hunks should be left expanded"
11174        );
11175    });
11176
11177    cx.update_editor(|editor, cx| {
11178        editor.select_all(&SelectAll, cx);
11179        editor.unfold_lines(&UnfoldLines, cx);
11180    });
11181    cx.executor().run_until_parked();
11182    cx.assert_editor_state(
11183        &r#"
11184        «use some::mod2;
11185
11186        const A: u32 = 42;
11187        const C: u32 = 42;
11188
11189        fn main() {
11190            //println!("hello");
11191
11192            println!("world");
11193            //
11194            //
11195        }
11196
11197        fn another() {
11198            println!("another");
11199            println!("another");
11200        }
11201
11202            println!("another2");
11203        }
11204        ˇ»"#
11205        .unindent(),
11206    );
11207    cx.update_editor(|editor, cx| {
11208        let snapshot = editor.snapshot(cx);
11209        let all_hunks = editor_hunks(editor, &snapshot, cx);
11210        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11211        assert_eq!(
11212            expanded_hunks_background_highlights(editor, cx),
11213            vec![
11214                DisplayRow(9)..=DisplayRow(10),
11215                DisplayRow(13)..=DisplayRow(14),
11216                DisplayRow(19)..=DisplayRow(19)
11217            ],
11218            "After unfolding, all hunk diffs should be visible again"
11219        );
11220        assert_eq!(
11221            all_hunks,
11222            vec![
11223                (
11224                    "use some::mod1;\n".to_string(),
11225                    DiffHunkStatus::Removed,
11226                    DisplayRow(1)..DisplayRow(1)
11227                ),
11228                (
11229                    "const B: u32 = 42;\n".to_string(),
11230                    DiffHunkStatus::Removed,
11231                    DisplayRow(5)..DisplayRow(5)
11232                ),
11233                (
11234                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11235                    DiffHunkStatus::Modified,
11236                    DisplayRow(9)..DisplayRow(11)
11237                ),
11238                (
11239                    "".to_string(),
11240                    DiffHunkStatus::Added,
11241                    DisplayRow(13)..DisplayRow(15)
11242                ),
11243                (
11244                    "".to_string(),
11245                    DiffHunkStatus::Added,
11246                    DisplayRow(19)..DisplayRow(20)
11247                ),
11248                (
11249                    "fn another2() {\n".to_string(),
11250                    DiffHunkStatus::Removed,
11251                    DisplayRow(23)..DisplayRow(23)
11252                ),
11253            ],
11254        );
11255        assert_eq!(all_hunks, all_expanded_hunks);
11256    });
11257}
11258
11259#[gpui::test]
11260async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11261    init_test(cx, |_| {});
11262
11263    let cols = 4;
11264    let rows = 10;
11265    let sample_text_1 = sample_text(rows, cols, 'a');
11266    assert_eq!(
11267        sample_text_1,
11268        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11269    );
11270    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11271    let sample_text_2 = sample_text(rows, cols, 'l');
11272    assert_eq!(
11273        sample_text_2,
11274        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11275    );
11276    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11277    let sample_text_3 = sample_text(rows, cols, 'v');
11278    assert_eq!(
11279        sample_text_3,
11280        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11281    );
11282    let modified_sample_text_3 =
11283        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11284    let buffer_1 = cx.new_model(|cx| {
11285        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11286        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11287        buffer
11288    });
11289    let buffer_2 = cx.new_model(|cx| {
11290        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11291        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11292        buffer
11293    });
11294    let buffer_3 = cx.new_model(|cx| {
11295        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11296        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11297        buffer
11298    });
11299
11300    let multi_buffer = cx.new_model(|cx| {
11301        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11302        multibuffer.push_excerpts(
11303            buffer_1.clone(),
11304            [
11305                ExcerptRange {
11306                    context: Point::new(0, 0)..Point::new(3, 0),
11307                    primary: None,
11308                },
11309                ExcerptRange {
11310                    context: Point::new(5, 0)..Point::new(7, 0),
11311                    primary: None,
11312                },
11313                ExcerptRange {
11314                    context: Point::new(9, 0)..Point::new(10, 4),
11315                    primary: None,
11316                },
11317            ],
11318            cx,
11319        );
11320        multibuffer.push_excerpts(
11321            buffer_2.clone(),
11322            [
11323                ExcerptRange {
11324                    context: Point::new(0, 0)..Point::new(3, 0),
11325                    primary: None,
11326                },
11327                ExcerptRange {
11328                    context: Point::new(5, 0)..Point::new(7, 0),
11329                    primary: None,
11330                },
11331                ExcerptRange {
11332                    context: Point::new(9, 0)..Point::new(10, 4),
11333                    primary: None,
11334                },
11335            ],
11336            cx,
11337        );
11338        multibuffer.push_excerpts(
11339            buffer_3.clone(),
11340            [
11341                ExcerptRange {
11342                    context: Point::new(0, 0)..Point::new(3, 0),
11343                    primary: None,
11344                },
11345                ExcerptRange {
11346                    context: Point::new(5, 0)..Point::new(7, 0),
11347                    primary: None,
11348                },
11349                ExcerptRange {
11350                    context: Point::new(9, 0)..Point::new(10, 4),
11351                    primary: None,
11352                },
11353            ],
11354            cx,
11355        );
11356        multibuffer
11357    });
11358
11359    let fs = FakeFs::new(cx.executor());
11360    fs.insert_tree(
11361        "/a",
11362        json!({
11363            "main.rs": modified_sample_text_1,
11364            "other.rs": modified_sample_text_2,
11365            "lib.rs": modified_sample_text_3,
11366        }),
11367    )
11368    .await;
11369
11370    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11371    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11372    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11373    let multi_buffer_editor = cx.new_view(|cx| {
11374        Editor::new(
11375            EditorMode::Full,
11376            multi_buffer,
11377            Some(project.clone()),
11378            true,
11379            cx,
11380        )
11381    });
11382    cx.executor().run_until_parked();
11383
11384    let expected_all_hunks = vec![
11385        (
11386            "bbbb\n".to_string(),
11387            DiffHunkStatus::Removed,
11388            DisplayRow(4)..DisplayRow(4),
11389        ),
11390        (
11391            "nnnn\n".to_string(),
11392            DiffHunkStatus::Modified,
11393            DisplayRow(21)..DisplayRow(22),
11394        ),
11395        (
11396            "".to_string(),
11397            DiffHunkStatus::Added,
11398            DisplayRow(41)..DisplayRow(42),
11399        ),
11400    ];
11401    let expected_all_hunks_shifted = vec![
11402        (
11403            "bbbb\n".to_string(),
11404            DiffHunkStatus::Removed,
11405            DisplayRow(5)..DisplayRow(5),
11406        ),
11407        (
11408            "nnnn\n".to_string(),
11409            DiffHunkStatus::Modified,
11410            DisplayRow(23)..DisplayRow(24),
11411        ),
11412        (
11413            "".to_string(),
11414            DiffHunkStatus::Added,
11415            DisplayRow(43)..DisplayRow(44),
11416        ),
11417    ];
11418
11419    multi_buffer_editor.update(cx, |editor, cx| {
11420        let snapshot = editor.snapshot(cx);
11421        let all_hunks = editor_hunks(editor, &snapshot, cx);
11422        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11423        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11424        assert_eq!(all_hunks, expected_all_hunks);
11425        assert_eq!(all_expanded_hunks, Vec::new());
11426    });
11427
11428    multi_buffer_editor.update(cx, |editor, cx| {
11429        editor.select_all(&SelectAll, cx);
11430        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11431    });
11432    cx.executor().run_until_parked();
11433    multi_buffer_editor.update(cx, |editor, cx| {
11434        let snapshot = editor.snapshot(cx);
11435        let all_hunks = editor_hunks(editor, &snapshot, cx);
11436        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11437        assert_eq!(
11438            expanded_hunks_background_highlights(editor, cx),
11439            vec![
11440                DisplayRow(23)..=DisplayRow(23),
11441                DisplayRow(43)..=DisplayRow(43)
11442            ],
11443        );
11444        assert_eq!(all_hunks, expected_all_hunks_shifted);
11445        assert_eq!(all_hunks, all_expanded_hunks);
11446    });
11447
11448    multi_buffer_editor.update(cx, |editor, cx| {
11449        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11450    });
11451    cx.executor().run_until_parked();
11452    multi_buffer_editor.update(cx, |editor, cx| {
11453        let snapshot = editor.snapshot(cx);
11454        let all_hunks = editor_hunks(editor, &snapshot, cx);
11455        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11456        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11457        assert_eq!(all_hunks, expected_all_hunks);
11458        assert_eq!(all_expanded_hunks, Vec::new());
11459    });
11460
11461    multi_buffer_editor.update(cx, |editor, cx| {
11462        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11463    });
11464    cx.executor().run_until_parked();
11465    multi_buffer_editor.update(cx, |editor, cx| {
11466        let snapshot = editor.snapshot(cx);
11467        let all_hunks = editor_hunks(editor, &snapshot, cx);
11468        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11469        assert_eq!(
11470            expanded_hunks_background_highlights(editor, cx),
11471            vec![
11472                DisplayRow(23)..=DisplayRow(23),
11473                DisplayRow(43)..=DisplayRow(43)
11474            ],
11475        );
11476        assert_eq!(all_hunks, expected_all_hunks_shifted);
11477        assert_eq!(all_hunks, all_expanded_hunks);
11478    });
11479
11480    multi_buffer_editor.update(cx, |editor, cx| {
11481        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11482    });
11483    cx.executor().run_until_parked();
11484    multi_buffer_editor.update(cx, |editor, cx| {
11485        let snapshot = editor.snapshot(cx);
11486        let all_hunks = editor_hunks(editor, &snapshot, cx);
11487        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11488        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11489        assert_eq!(all_hunks, expected_all_hunks);
11490        assert_eq!(all_expanded_hunks, Vec::new());
11491    });
11492}
11493
11494#[gpui::test]
11495async fn test_edits_around_toggled_additions(
11496    executor: BackgroundExecutor,
11497    cx: &mut gpui::TestAppContext,
11498) {
11499    init_test(cx, |_| {});
11500
11501    let mut cx = EditorTestContext::new(cx).await;
11502
11503    let diff_base = r#"
11504        use some::mod1;
11505        use some::mod2;
11506
11507        const A: u32 = 42;
11508
11509        fn main() {
11510            println!("hello");
11511
11512            println!("world");
11513        }
11514        "#
11515    .unindent();
11516    executor.run_until_parked();
11517    cx.set_state(
11518        &r#"
11519        use some::mod1;
11520        use some::mod2;
11521
11522        const A: u32 = 42;
11523        const B: u32 = 42;
11524        const C: u32 = 42;
11525        ˇ
11526
11527        fn main() {
11528            println!("hello");
11529
11530            println!("world");
11531        }
11532        "#
11533        .unindent(),
11534    );
11535
11536    cx.set_diff_base(Some(&diff_base));
11537    executor.run_until_parked();
11538    cx.update_editor(|editor, cx| {
11539        let snapshot = editor.snapshot(cx);
11540        let all_hunks = editor_hunks(editor, &snapshot, cx);
11541        assert_eq!(
11542            all_hunks,
11543            vec![(
11544                "".to_string(),
11545                DiffHunkStatus::Added,
11546                DisplayRow(4)..DisplayRow(7)
11547            )]
11548        );
11549    });
11550    cx.update_editor(|editor, cx| {
11551        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11552    });
11553    executor.run_until_parked();
11554    cx.assert_editor_state(
11555        &r#"
11556        use some::mod1;
11557        use some::mod2;
11558
11559        const A: u32 = 42;
11560        const B: u32 = 42;
11561        const C: u32 = 42;
11562        ˇ
11563
11564        fn main() {
11565            println!("hello");
11566
11567            println!("world");
11568        }
11569        "#
11570        .unindent(),
11571    );
11572    cx.update_editor(|editor, cx| {
11573        let snapshot = editor.snapshot(cx);
11574        let all_hunks = editor_hunks(editor, &snapshot, cx);
11575        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11576        assert_eq!(
11577            all_hunks,
11578            vec![(
11579                "".to_string(),
11580                DiffHunkStatus::Added,
11581                DisplayRow(4)..DisplayRow(7)
11582            )]
11583        );
11584        assert_eq!(
11585            expanded_hunks_background_highlights(editor, cx),
11586            vec![DisplayRow(4)..=DisplayRow(6)]
11587        );
11588        assert_eq!(all_hunks, all_expanded_hunks);
11589    });
11590
11591    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11592    executor.run_until_parked();
11593    cx.assert_editor_state(
11594        &r#"
11595        use some::mod1;
11596        use some::mod2;
11597
11598        const A: u32 = 42;
11599        const B: u32 = 42;
11600        const C: u32 = 42;
11601        const D: u32 = 42;
11602        ˇ
11603
11604        fn main() {
11605            println!("hello");
11606
11607            println!("world");
11608        }
11609        "#
11610        .unindent(),
11611    );
11612    cx.update_editor(|editor, cx| {
11613        let snapshot = editor.snapshot(cx);
11614        let all_hunks = editor_hunks(editor, &snapshot, cx);
11615        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11616        assert_eq!(
11617            all_hunks,
11618            vec![(
11619                "".to_string(),
11620                DiffHunkStatus::Added,
11621                DisplayRow(4)..DisplayRow(8)
11622            )]
11623        );
11624        assert_eq!(
11625            expanded_hunks_background_highlights(editor, cx),
11626            vec![DisplayRow(4)..=DisplayRow(6)],
11627            "Edited hunk should have one more line added"
11628        );
11629        assert_eq!(
11630            all_hunks, all_expanded_hunks,
11631            "Expanded hunk should also grow with the addition"
11632        );
11633    });
11634
11635    cx.update_editor(|editor, cx| editor.handle_input("const E: 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        const E: u32 = 42;
11647        ˇ
11648
11649        fn main() {
11650            println!("hello");
11651
11652            println!("world");
11653        }
11654        "#
11655        .unindent(),
11656    );
11657    cx.update_editor(|editor, cx| {
11658        let snapshot = editor.snapshot(cx);
11659        let all_hunks = editor_hunks(editor, &snapshot, cx);
11660        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11661        assert_eq!(
11662            all_hunks,
11663            vec![(
11664                "".to_string(),
11665                DiffHunkStatus::Added,
11666                DisplayRow(4)..DisplayRow(9)
11667            )]
11668        );
11669        assert_eq!(
11670            expanded_hunks_background_highlights(editor, cx),
11671            vec![DisplayRow(4)..=DisplayRow(6)],
11672            "Edited hunk should have one more line added"
11673        );
11674        assert_eq!(all_hunks, all_expanded_hunks);
11675    });
11676
11677    cx.update_editor(|editor, cx| {
11678        editor.move_up(&MoveUp, cx);
11679        editor.delete_line(&DeleteLine, cx);
11680    });
11681    executor.run_until_parked();
11682    cx.assert_editor_state(
11683        &r#"
11684        use some::mod1;
11685        use some::mod2;
11686
11687        const A: u32 = 42;
11688        const B: u32 = 42;
11689        const C: u32 = 42;
11690        const D: 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(8)
11711            )]
11712        );
11713        assert_eq!(
11714            expanded_hunks_background_highlights(editor, cx),
11715            vec![DisplayRow(4)..=DisplayRow(6)],
11716            "Deleting a line should shrint the hunk"
11717        );
11718        assert_eq!(
11719            all_hunks, all_expanded_hunks,
11720            "Expanded hunk should also shrink with the addition"
11721        );
11722    });
11723
11724    cx.update_editor(|editor, cx| {
11725        editor.move_up(&MoveUp, cx);
11726        editor.delete_line(&DeleteLine, cx);
11727        editor.move_up(&MoveUp, cx);
11728        editor.delete_line(&DeleteLine, cx);
11729        editor.move_up(&MoveUp, cx);
11730        editor.delete_line(&DeleteLine, cx);
11731    });
11732    executor.run_until_parked();
11733    cx.assert_editor_state(
11734        &r#"
11735        use some::mod1;
11736        use some::mod2;
11737
11738        const A: u32 = 42;
11739        ˇ
11740
11741        fn main() {
11742            println!("hello");
11743
11744            println!("world");
11745        }
11746        "#
11747        .unindent(),
11748    );
11749    cx.update_editor(|editor, cx| {
11750        let snapshot = editor.snapshot(cx);
11751        let all_hunks = editor_hunks(editor, &snapshot, cx);
11752        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11753        assert_eq!(
11754            all_hunks,
11755            vec![(
11756                "".to_string(),
11757                DiffHunkStatus::Added,
11758                DisplayRow(5)..DisplayRow(6)
11759            )]
11760        );
11761        assert_eq!(
11762            expanded_hunks_background_highlights(editor, cx),
11763            vec![DisplayRow(5)..=DisplayRow(5)]
11764        );
11765        assert_eq!(all_hunks, all_expanded_hunks);
11766    });
11767
11768    cx.update_editor(|editor, cx| {
11769        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11770        editor.delete_line(&DeleteLine, cx);
11771    });
11772    executor.run_until_parked();
11773    cx.assert_editor_state(
11774        &r#"
11775        ˇ
11776
11777        fn main() {
11778            println!("hello");
11779
11780            println!("world");
11781        }
11782        "#
11783        .unindent(),
11784    );
11785    cx.update_editor(|editor, cx| {
11786        let snapshot = editor.snapshot(cx);
11787        let all_hunks = editor_hunks(editor, &snapshot, cx);
11788        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11789        assert_eq!(
11790            all_hunks,
11791            vec![
11792                (
11793                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11794                    DiffHunkStatus::Removed,
11795                    DisplayRow(0)..DisplayRow(0)
11796                ),
11797                (
11798                    "const A: u32 = 42;\n".to_string(),
11799                    DiffHunkStatus::Removed,
11800                    DisplayRow(2)..DisplayRow(2)
11801                )
11802            ]
11803        );
11804        assert_eq!(
11805            expanded_hunks_background_highlights(editor, cx),
11806            Vec::new(),
11807            "Should close all stale expanded addition hunks"
11808        );
11809        assert_eq!(
11810            all_expanded_hunks,
11811            vec![(
11812                "const A: u32 = 42;\n".to_string(),
11813                DiffHunkStatus::Removed,
11814                DisplayRow(2)..DisplayRow(2)
11815            )],
11816            "Should open hunks that were adjacent to the stale addition one"
11817        );
11818    });
11819}
11820
11821#[gpui::test]
11822async fn test_edits_around_toggled_deletions(
11823    executor: BackgroundExecutor,
11824    cx: &mut gpui::TestAppContext,
11825) {
11826    init_test(cx, |_| {});
11827
11828    let mut cx = EditorTestContext::new(cx).await;
11829
11830    let diff_base = r#"
11831        use some::mod1;
11832        use some::mod2;
11833
11834        const A: u32 = 42;
11835        const B: u32 = 42;
11836        const C: u32 = 42;
11837
11838
11839        fn main() {
11840            println!("hello");
11841
11842            println!("world");
11843        }
11844        "#
11845    .unindent();
11846    executor.run_until_parked();
11847    cx.set_state(
11848        &r#"
11849        use some::mod1;
11850        use some::mod2;
11851
11852        ˇconst B: u32 = 42;
11853        const C: u32 = 42;
11854
11855
11856        fn main() {
11857            println!("hello");
11858
11859            println!("world");
11860        }
11861        "#
11862        .unindent(),
11863    );
11864
11865    cx.set_diff_base(Some(&diff_base));
11866    executor.run_until_parked();
11867    cx.update_editor(|editor, cx| {
11868        let snapshot = editor.snapshot(cx);
11869        let all_hunks = editor_hunks(editor, &snapshot, cx);
11870        assert_eq!(
11871            all_hunks,
11872            vec![(
11873                "const A: u32 = 42;\n".to_string(),
11874                DiffHunkStatus::Removed,
11875                DisplayRow(3)..DisplayRow(3)
11876            )]
11877        );
11878    });
11879    cx.update_editor(|editor, cx| {
11880        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11881    });
11882    executor.run_until_parked();
11883    cx.assert_editor_state(
11884        &r#"
11885        use some::mod1;
11886        use some::mod2;
11887
11888        ˇconst B: u32 = 42;
11889        const C: u32 = 42;
11890
11891
11892        fn main() {
11893            println!("hello");
11894
11895            println!("world");
11896        }
11897        "#
11898        .unindent(),
11899    );
11900    cx.update_editor(|editor, cx| {
11901        let snapshot = editor.snapshot(cx);
11902        let all_hunks = editor_hunks(editor, &snapshot, cx);
11903        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11904        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11905        assert_eq!(
11906            all_hunks,
11907            vec![(
11908                "const A: u32 = 42;\n".to_string(),
11909                DiffHunkStatus::Removed,
11910                DisplayRow(4)..DisplayRow(4)
11911            )]
11912        );
11913        assert_eq!(all_hunks, all_expanded_hunks);
11914    });
11915
11916    cx.update_editor(|editor, cx| {
11917        editor.delete_line(&DeleteLine, cx);
11918    });
11919    executor.run_until_parked();
11920    cx.assert_editor_state(
11921        &r#"
11922        use some::mod1;
11923        use some::mod2;
11924
11925        ˇconst C: u32 = 42;
11926
11927
11928        fn main() {
11929            println!("hello");
11930
11931            println!("world");
11932        }
11933        "#
11934        .unindent(),
11935    );
11936    cx.update_editor(|editor, cx| {
11937        let snapshot = editor.snapshot(cx);
11938        let all_hunks = editor_hunks(editor, &snapshot, cx);
11939        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11940        assert_eq!(
11941            expanded_hunks_background_highlights(editor, cx),
11942            Vec::new(),
11943            "Deleted hunks do not highlight current editor's background"
11944        );
11945        assert_eq!(
11946            all_hunks,
11947            vec![(
11948                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11949                DiffHunkStatus::Removed,
11950                DisplayRow(5)..DisplayRow(5)
11951            )]
11952        );
11953        assert_eq!(all_hunks, all_expanded_hunks);
11954    });
11955
11956    cx.update_editor(|editor, cx| {
11957        editor.delete_line(&DeleteLine, cx);
11958    });
11959    executor.run_until_parked();
11960    cx.assert_editor_state(
11961        &r#"
11962        use some::mod1;
11963        use some::mod2;
11964
11965        ˇ
11966
11967        fn main() {
11968            println!("hello");
11969
11970            println!("world");
11971        }
11972        "#
11973        .unindent(),
11974    );
11975    cx.update_editor(|editor, cx| {
11976        let snapshot = editor.snapshot(cx);
11977        let all_hunks = editor_hunks(editor, &snapshot, cx);
11978        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11979        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11980        assert_eq!(
11981            all_hunks,
11982            vec![(
11983                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11984                DiffHunkStatus::Removed,
11985                DisplayRow(6)..DisplayRow(6)
11986            )]
11987        );
11988        assert_eq!(all_hunks, all_expanded_hunks);
11989    });
11990
11991    cx.update_editor(|editor, cx| {
11992        editor.handle_input("replacement", cx);
11993    });
11994    executor.run_until_parked();
11995    cx.assert_editor_state(
11996        &r#"
11997        use some::mod1;
11998        use some::mod2;
11999
12000        replacementˇ
12001
12002        fn main() {
12003            println!("hello");
12004
12005            println!("world");
12006        }
12007        "#
12008        .unindent(),
12009    );
12010    cx.update_editor(|editor, cx| {
12011        let snapshot = editor.snapshot(cx);
12012        let all_hunks = editor_hunks(editor, &snapshot, cx);
12013        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12014        assert_eq!(
12015            all_hunks,
12016            vec![(
12017                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12018                DiffHunkStatus::Modified,
12019                DisplayRow(7)..DisplayRow(8)
12020            )]
12021        );
12022        assert_eq!(
12023            expanded_hunks_background_highlights(editor, cx),
12024            vec![DisplayRow(7)..=DisplayRow(7)],
12025            "Modified expanded hunks should display additions and highlight their background"
12026        );
12027        assert_eq!(all_hunks, all_expanded_hunks);
12028    });
12029}
12030
12031#[gpui::test]
12032async fn test_edits_around_toggled_modifications(
12033    executor: BackgroundExecutor,
12034    cx: &mut gpui::TestAppContext,
12035) {
12036    init_test(cx, |_| {});
12037
12038    let mut cx = EditorTestContext::new(cx).await;
12039
12040    let diff_base = r#"
12041        use some::mod1;
12042        use some::mod2;
12043
12044        const A: u32 = 42;
12045        const B: u32 = 42;
12046        const C: u32 = 42;
12047        const D: u32 = 42;
12048
12049
12050        fn main() {
12051            println!("hello");
12052
12053            println!("world");
12054        }"#
12055    .unindent();
12056    executor.run_until_parked();
12057    cx.set_state(
12058        &r#"
12059        use some::mod1;
12060        use some::mod2;
12061
12062        const A: u32 = 42;
12063        const B: u32 = 42;
12064        const C: u32 = 43ˇ
12065        const D: u32 = 42;
12066
12067
12068        fn main() {
12069            println!("hello");
12070
12071            println!("world");
12072        }"#
12073        .unindent(),
12074    );
12075
12076    cx.set_diff_base(Some(&diff_base));
12077    executor.run_until_parked();
12078    cx.update_editor(|editor, cx| {
12079        let snapshot = editor.snapshot(cx);
12080        let all_hunks = editor_hunks(editor, &snapshot, cx);
12081        assert_eq!(
12082            all_hunks,
12083            vec![(
12084                "const C: u32 = 42;\n".to_string(),
12085                DiffHunkStatus::Modified,
12086                DisplayRow(5)..DisplayRow(6)
12087            )]
12088        );
12089    });
12090    cx.update_editor(|editor, cx| {
12091        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12092    });
12093    executor.run_until_parked();
12094    cx.assert_editor_state(
12095        &r#"
12096        use some::mod1;
12097        use some::mod2;
12098
12099        const A: u32 = 42;
12100        const B: u32 = 42;
12101        const C: u32 = 43ˇ
12102        const D: u32 = 42;
12103
12104
12105        fn main() {
12106            println!("hello");
12107
12108            println!("world");
12109        }"#
12110        .unindent(),
12111    );
12112    cx.update_editor(|editor, cx| {
12113        let snapshot = editor.snapshot(cx);
12114        let all_hunks = editor_hunks(editor, &snapshot, cx);
12115        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12116        assert_eq!(
12117            expanded_hunks_background_highlights(editor, cx),
12118            vec![DisplayRow(6)..=DisplayRow(6)],
12119        );
12120        assert_eq!(
12121            all_hunks,
12122            vec![(
12123                "const C: u32 = 42;\n".to_string(),
12124                DiffHunkStatus::Modified,
12125                DisplayRow(6)..DisplayRow(7)
12126            )]
12127        );
12128        assert_eq!(all_hunks, all_expanded_hunks);
12129    });
12130
12131    cx.update_editor(|editor, cx| {
12132        editor.handle_input("\nnew_line\n", cx);
12133    });
12134    executor.run_until_parked();
12135    cx.assert_editor_state(
12136        &r#"
12137            use some::mod1;
12138            use some::mod2;
12139
12140            const A: u32 = 42;
12141            const B: u32 = 42;
12142            const C: u32 = 43
12143            new_line
12144            ˇ
12145            const D: u32 = 42;
12146
12147
12148            fn main() {
12149                println!("hello");
12150
12151                println!("world");
12152            }"#
12153        .unindent(),
12154    );
12155    cx.update_editor(|editor, cx| {
12156        let snapshot = editor.snapshot(cx);
12157        let all_hunks = editor_hunks(editor, &snapshot, cx);
12158        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12159        assert_eq!(
12160            expanded_hunks_background_highlights(editor, cx),
12161            vec![DisplayRow(6)..=DisplayRow(6)],
12162            "Modified hunk should grow highlighted lines on more text additions"
12163        );
12164        assert_eq!(
12165            all_hunks,
12166            vec![(
12167                "const C: u32 = 42;\n".to_string(),
12168                DiffHunkStatus::Modified,
12169                DisplayRow(6)..DisplayRow(9)
12170            )]
12171        );
12172        assert_eq!(all_hunks, all_expanded_hunks);
12173    });
12174
12175    cx.update_editor(|editor, cx| {
12176        editor.move_up(&MoveUp, cx);
12177        editor.move_up(&MoveUp, cx);
12178        editor.move_up(&MoveUp, cx);
12179        editor.delete_line(&DeleteLine, cx);
12180    });
12181    executor.run_until_parked();
12182    cx.assert_editor_state(
12183        &r#"
12184            use some::mod1;
12185            use some::mod2;
12186
12187            const A: u32 = 42;
12188            ˇconst C: u32 = 43
12189            new_line
12190
12191            const D: u32 = 42;
12192
12193
12194            fn main() {
12195                println!("hello");
12196
12197                println!("world");
12198            }"#
12199        .unindent(),
12200    );
12201    cx.update_editor(|editor, cx| {
12202        let snapshot = editor.snapshot(cx);
12203        let all_hunks = editor_hunks(editor, &snapshot, cx);
12204        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12205        assert_eq!(
12206            expanded_hunks_background_highlights(editor, cx),
12207            vec![DisplayRow(6)..=DisplayRow(8)],
12208        );
12209        assert_eq!(
12210            all_hunks,
12211            vec![(
12212                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12213                DiffHunkStatus::Modified,
12214                DisplayRow(6)..DisplayRow(9)
12215            )],
12216            "Modified hunk should grow deleted lines on text deletions above"
12217        );
12218        assert_eq!(all_hunks, all_expanded_hunks);
12219    });
12220
12221    cx.update_editor(|editor, cx| {
12222        editor.move_up(&MoveUp, cx);
12223        editor.handle_input("v", cx);
12224    });
12225    executor.run_until_parked();
12226    cx.assert_editor_state(
12227        &r#"
12228            use some::mod1;
12229            use some::mod2;
12230
12231            vˇ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(9)],
12252            "Modified hunk should grow deleted lines on text modifications above"
12253        );
12254        assert_eq!(
12255            all_hunks,
12256            vec![(
12257                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12258                DiffHunkStatus::Modified,
12259                DisplayRow(6)..DisplayRow(10)
12260            )]
12261        );
12262        assert_eq!(all_hunks, all_expanded_hunks);
12263    });
12264
12265    cx.update_editor(|editor, cx| {
12266        editor.move_down(&MoveDown, cx);
12267        editor.move_down(&MoveDown, cx);
12268        editor.delete_line(&DeleteLine, cx)
12269    });
12270    executor.run_until_parked();
12271    cx.assert_editor_state(
12272        &r#"
12273            use some::mod1;
12274            use some::mod2;
12275
12276            vconst A: u32 = 42;
12277            const C: u32 = 43
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(8)],
12296            "Modified hunk should grow shrink lines on modification lines removal"
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(9)
12304            )]
12305        );
12306        assert_eq!(all_hunks, all_expanded_hunks);
12307    });
12308
12309    cx.update_editor(|editor, cx| {
12310        editor.move_up(&MoveUp, cx);
12311        editor.move_up(&MoveUp, cx);
12312        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12313        editor.delete_line(&DeleteLine, cx)
12314    });
12315    executor.run_until_parked();
12316    cx.assert_editor_state(
12317        &r#"
12318            use some::mod1;
12319            use some::mod2;
12320
12321            ˇ
12322
12323            fn main() {
12324                println!("hello");
12325
12326                println!("world");
12327            }"#
12328        .unindent(),
12329    );
12330    cx.update_editor(|editor, cx| {
12331        let snapshot = editor.snapshot(cx);
12332        let all_hunks = editor_hunks(editor, &snapshot, cx);
12333        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12334        assert_eq!(
12335            expanded_hunks_background_highlights(editor, cx),
12336            Vec::new(),
12337            "Modified hunk should turn into a removed one on all modified lines removal"
12338        );
12339        assert_eq!(
12340            all_hunks,
12341            vec![(
12342                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12343                    .to_string(),
12344                DiffHunkStatus::Removed,
12345                DisplayRow(7)..DisplayRow(7)
12346            )]
12347        );
12348        assert_eq!(all_hunks, all_expanded_hunks);
12349    });
12350}
12351
12352#[gpui::test]
12353async fn test_multiple_expanded_hunks_merge(
12354    executor: BackgroundExecutor,
12355    cx: &mut gpui::TestAppContext,
12356) {
12357    init_test(cx, |_| {});
12358
12359    let mut cx = EditorTestContext::new(cx).await;
12360
12361    let diff_base = r#"
12362        use some::mod1;
12363        use some::mod2;
12364
12365        const A: u32 = 42;
12366        const B: u32 = 42;
12367        const C: u32 = 42;
12368        const D: u32 = 42;
12369
12370
12371        fn main() {
12372            println!("hello");
12373
12374            println!("world");
12375        }"#
12376    .unindent();
12377    executor.run_until_parked();
12378    cx.set_state(
12379        &r#"
12380        use some::mod1;
12381        use some::mod2;
12382
12383        const A: u32 = 42;
12384        const B: u32 = 42;
12385        const C: u32 = 43ˇ
12386        const D: u32 = 42;
12387
12388
12389        fn main() {
12390            println!("hello");
12391
12392            println!("world");
12393        }"#
12394        .unindent(),
12395    );
12396
12397    cx.set_diff_base(Some(&diff_base));
12398    executor.run_until_parked();
12399    cx.update_editor(|editor, cx| {
12400        let snapshot = editor.snapshot(cx);
12401        let all_hunks = editor_hunks(editor, &snapshot, cx);
12402        assert_eq!(
12403            all_hunks,
12404            vec![(
12405                "const C: u32 = 42;\n".to_string(),
12406                DiffHunkStatus::Modified,
12407                DisplayRow(5)..DisplayRow(6)
12408            )]
12409        );
12410    });
12411    cx.update_editor(|editor, cx| {
12412        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12413    });
12414    executor.run_until_parked();
12415    cx.assert_editor_state(
12416        &r#"
12417        use some::mod1;
12418        use some::mod2;
12419
12420        const A: u32 = 42;
12421        const B: u32 = 42;
12422        const C: u32 = 43ˇ
12423        const D: u32 = 42;
12424
12425
12426        fn main() {
12427            println!("hello");
12428
12429            println!("world");
12430        }"#
12431        .unindent(),
12432    );
12433    cx.update_editor(|editor, cx| {
12434        let snapshot = editor.snapshot(cx);
12435        let all_hunks = editor_hunks(editor, &snapshot, cx);
12436        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12437        assert_eq!(
12438            expanded_hunks_background_highlights(editor, cx),
12439            vec![DisplayRow(6)..=DisplayRow(6)],
12440        );
12441        assert_eq!(
12442            all_hunks,
12443            vec![(
12444                "const C: u32 = 42;\n".to_string(),
12445                DiffHunkStatus::Modified,
12446                DisplayRow(6)..DisplayRow(7)
12447            )]
12448        );
12449        assert_eq!(all_hunks, all_expanded_hunks);
12450    });
12451
12452    cx.update_editor(|editor, cx| {
12453        editor.handle_input("\nnew_line\n", cx);
12454    });
12455    executor.run_until_parked();
12456    cx.assert_editor_state(
12457        &r#"
12458            use some::mod1;
12459            use some::mod2;
12460
12461            const A: u32 = 42;
12462            const B: u32 = 42;
12463            const C: u32 = 43
12464            new_line
12465            ˇ
12466            const D: u32 = 42;
12467
12468
12469            fn main() {
12470                println!("hello");
12471
12472                println!("world");
12473            }"#
12474        .unindent(),
12475    );
12476}
12477
12478async fn setup_indent_guides_editor(
12479    text: &str,
12480    cx: &mut gpui::TestAppContext,
12481) -> (BufferId, EditorTestContext) {
12482    init_test(cx, |_| {});
12483
12484    let mut cx = EditorTestContext::new(cx).await;
12485
12486    let buffer_id = cx.update_editor(|editor, cx| {
12487        editor.set_text(text, cx);
12488        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12489        let buffer_id = buffer_ids[0];
12490        buffer_id
12491    });
12492
12493    (buffer_id, cx)
12494}
12495
12496fn assert_indent_guides(
12497    range: Range<u32>,
12498    expected: Vec<IndentGuide>,
12499    active_indices: Option<Vec<usize>>,
12500    cx: &mut EditorTestContext,
12501) {
12502    let indent_guides = cx.update_editor(|editor, cx| {
12503        let snapshot = editor.snapshot(cx).display_snapshot;
12504        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12505            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12506            true,
12507            &snapshot,
12508            cx,
12509        );
12510
12511        indent_guides.sort_by(|a, b| {
12512            a.depth.cmp(&b.depth).then(
12513                a.start_row
12514                    .cmp(&b.start_row)
12515                    .then(a.end_row.cmp(&b.end_row)),
12516            )
12517        });
12518        indent_guides
12519    });
12520
12521    if let Some(expected) = active_indices {
12522        let active_indices = cx.update_editor(|editor, cx| {
12523            let snapshot = editor.snapshot(cx).display_snapshot;
12524            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12525        });
12526
12527        assert_eq!(
12528            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12529            expected,
12530            "Active indent guide indices do not match"
12531        );
12532    }
12533
12534    let expected: Vec<_> = expected
12535        .into_iter()
12536        .map(|guide| MultiBufferIndentGuide {
12537            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12538            buffer: guide,
12539        })
12540        .collect();
12541
12542    assert_eq!(indent_guides, expected, "Indent guides do not match");
12543}
12544
12545fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12546    IndentGuide {
12547        buffer_id,
12548        start_row,
12549        end_row,
12550        depth,
12551        tab_size: 4,
12552        settings: IndentGuideSettings {
12553            enabled: true,
12554            line_width: 1,
12555            active_line_width: 1,
12556            ..Default::default()
12557        },
12558    }
12559}
12560
12561#[gpui::test]
12562async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12563    let (buffer_id, mut cx) = setup_indent_guides_editor(
12564        &"
12565    fn main() {
12566        let a = 1;
12567    }"
12568        .unindent(),
12569        cx,
12570    )
12571    .await;
12572
12573    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12574}
12575
12576#[gpui::test]
12577async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12578    let (buffer_id, mut cx) = setup_indent_guides_editor(
12579        &"
12580    fn main() {
12581        let a = 1;
12582        let b = 2;
12583    }"
12584        .unindent(),
12585        cx,
12586    )
12587    .await;
12588
12589    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12590}
12591
12592#[gpui::test]
12593async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12594    let (buffer_id, mut cx) = setup_indent_guides_editor(
12595        &"
12596    fn main() {
12597        let a = 1;
12598        if a == 3 {
12599            let b = 2;
12600        } else {
12601            let c = 3;
12602        }
12603    }"
12604        .unindent(),
12605        cx,
12606    )
12607    .await;
12608
12609    assert_indent_guides(
12610        0..8,
12611        vec![
12612            indent_guide(buffer_id, 1, 6, 0),
12613            indent_guide(buffer_id, 3, 3, 1),
12614            indent_guide(buffer_id, 5, 5, 1),
12615        ],
12616        None,
12617        &mut cx,
12618    );
12619}
12620
12621#[gpui::test]
12622async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12623    let (buffer_id, mut cx) = setup_indent_guides_editor(
12624        &"
12625    fn main() {
12626        let a = 1;
12627            let b = 2;
12628        let c = 3;
12629    }"
12630        .unindent(),
12631        cx,
12632    )
12633    .await;
12634
12635    assert_indent_guides(
12636        0..5,
12637        vec![
12638            indent_guide(buffer_id, 1, 3, 0),
12639            indent_guide(buffer_id, 2, 2, 1),
12640        ],
12641        None,
12642        &mut cx,
12643    );
12644}
12645
12646#[gpui::test]
12647async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12648    let (buffer_id, mut cx) = setup_indent_guides_editor(
12649        &"
12650        fn main() {
12651            let a = 1;
12652
12653            let c = 3;
12654        }"
12655        .unindent(),
12656        cx,
12657    )
12658    .await;
12659
12660    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12661}
12662
12663#[gpui::test]
12664async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12665    let (buffer_id, mut cx) = setup_indent_guides_editor(
12666        &"
12667        fn main() {
12668            let a = 1;
12669
12670            let c = 3;
12671
12672            if a == 3 {
12673                let b = 2;
12674            } else {
12675                let c = 3;
12676            }
12677        }"
12678        .unindent(),
12679        cx,
12680    )
12681    .await;
12682
12683    assert_indent_guides(
12684        0..11,
12685        vec![
12686            indent_guide(buffer_id, 1, 9, 0),
12687            indent_guide(buffer_id, 6, 6, 1),
12688            indent_guide(buffer_id, 8, 8, 1),
12689        ],
12690        None,
12691        &mut cx,
12692    );
12693}
12694
12695#[gpui::test]
12696async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12697    let (buffer_id, mut cx) = setup_indent_guides_editor(
12698        &"
12699        fn main() {
12700            let a = 1;
12701
12702            let c = 3;
12703
12704            if a == 3 {
12705                let b = 2;
12706            } else {
12707                let c = 3;
12708            }
12709        }"
12710        .unindent(),
12711        cx,
12712    )
12713    .await;
12714
12715    assert_indent_guides(
12716        1..11,
12717        vec![
12718            indent_guide(buffer_id, 1, 9, 0),
12719            indent_guide(buffer_id, 6, 6, 1),
12720            indent_guide(buffer_id, 8, 8, 1),
12721        ],
12722        None,
12723        &mut cx,
12724    );
12725}
12726
12727#[gpui::test]
12728async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12729    let (buffer_id, mut cx) = setup_indent_guides_editor(
12730        &"
12731        fn main() {
12732            let a = 1;
12733
12734            let c = 3;
12735
12736            if a == 3 {
12737                let b = 2;
12738            } else {
12739                let c = 3;
12740            }
12741        }"
12742        .unindent(),
12743        cx,
12744    )
12745    .await;
12746
12747    assert_indent_guides(
12748        1..10,
12749        vec![
12750            indent_guide(buffer_id, 1, 9, 0),
12751            indent_guide(buffer_id, 6, 6, 1),
12752            indent_guide(buffer_id, 8, 8, 1),
12753        ],
12754        None,
12755        &mut cx,
12756    );
12757}
12758
12759#[gpui::test]
12760async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12761    let (buffer_id, mut cx) = setup_indent_guides_editor(
12762        &"
12763        block1
12764            block2
12765                block3
12766                    block4
12767            block2
12768        block1
12769        block1"
12770            .unindent(),
12771        cx,
12772    )
12773    .await;
12774
12775    assert_indent_guides(
12776        1..10,
12777        vec![
12778            indent_guide(buffer_id, 1, 4, 0),
12779            indent_guide(buffer_id, 2, 3, 1),
12780            indent_guide(buffer_id, 3, 3, 2),
12781        ],
12782        None,
12783        &mut cx,
12784    );
12785}
12786
12787#[gpui::test]
12788async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12789    let (buffer_id, mut cx) = setup_indent_guides_editor(
12790        &"
12791        block1
12792            block2
12793                block3
12794
12795        block1
12796        block1"
12797            .unindent(),
12798        cx,
12799    )
12800    .await;
12801
12802    assert_indent_guides(
12803        0..6,
12804        vec![
12805            indent_guide(buffer_id, 1, 2, 0),
12806            indent_guide(buffer_id, 2, 2, 1),
12807        ],
12808        None,
12809        &mut cx,
12810    );
12811}
12812
12813#[gpui::test]
12814async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12815    let (buffer_id, mut cx) = setup_indent_guides_editor(
12816        &"
12817        block1
12818
12819
12820
12821            block2
12822        "
12823        .unindent(),
12824        cx,
12825    )
12826    .await;
12827
12828    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12829}
12830
12831#[gpui::test]
12832async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12833    let (buffer_id, mut cx) = setup_indent_guides_editor(
12834        &"
12835        def a:
12836        \tb = 3
12837        \tif True:
12838        \t\tc = 4
12839        \t\td = 5
12840        \tprint(b)
12841        "
12842        .unindent(),
12843        cx,
12844    )
12845    .await;
12846
12847    assert_indent_guides(
12848        0..6,
12849        vec![
12850            indent_guide(buffer_id, 1, 6, 0),
12851            indent_guide(buffer_id, 3, 4, 1),
12852        ],
12853        None,
12854        &mut cx,
12855    );
12856}
12857
12858#[gpui::test]
12859async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12860    let (buffer_id, mut cx) = setup_indent_guides_editor(
12861        &"
12862    fn main() {
12863        let a = 1;
12864    }"
12865        .unindent(),
12866        cx,
12867    )
12868    .await;
12869
12870    cx.update_editor(|editor, cx| {
12871        editor.change_selections(None, cx, |s| {
12872            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12873        });
12874    });
12875
12876    assert_indent_guides(
12877        0..3,
12878        vec![indent_guide(buffer_id, 1, 1, 0)],
12879        Some(vec![0]),
12880        &mut cx,
12881    );
12882}
12883
12884#[gpui::test]
12885async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12886    let (buffer_id, mut cx) = setup_indent_guides_editor(
12887        &"
12888    fn main() {
12889        if 1 == 2 {
12890            let a = 1;
12891        }
12892    }"
12893        .unindent(),
12894        cx,
12895    )
12896    .await;
12897
12898    cx.update_editor(|editor, cx| {
12899        editor.change_selections(None, cx, |s| {
12900            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12901        });
12902    });
12903
12904    assert_indent_guides(
12905        0..4,
12906        vec![
12907            indent_guide(buffer_id, 1, 3, 0),
12908            indent_guide(buffer_id, 2, 2, 1),
12909        ],
12910        Some(vec![1]),
12911        &mut cx,
12912    );
12913
12914    cx.update_editor(|editor, cx| {
12915        editor.change_selections(None, cx, |s| {
12916            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12917        });
12918    });
12919
12920    assert_indent_guides(
12921        0..4,
12922        vec![
12923            indent_guide(buffer_id, 1, 3, 0),
12924            indent_guide(buffer_id, 2, 2, 1),
12925        ],
12926        Some(vec![1]),
12927        &mut cx,
12928    );
12929
12930    cx.update_editor(|editor, cx| {
12931        editor.change_selections(None, cx, |s| {
12932            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12933        });
12934    });
12935
12936    assert_indent_guides(
12937        0..4,
12938        vec![
12939            indent_guide(buffer_id, 1, 3, 0),
12940            indent_guide(buffer_id, 2, 2, 1),
12941        ],
12942        Some(vec![0]),
12943        &mut cx,
12944    );
12945}
12946
12947#[gpui::test]
12948async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12949    let (buffer_id, mut cx) = setup_indent_guides_editor(
12950        &"
12951    fn main() {
12952        let a = 1;
12953
12954        let b = 2;
12955    }"
12956        .unindent(),
12957        cx,
12958    )
12959    .await;
12960
12961    cx.update_editor(|editor, cx| {
12962        editor.change_selections(None, cx, |s| {
12963            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12964        });
12965    });
12966
12967    assert_indent_guides(
12968        0..5,
12969        vec![indent_guide(buffer_id, 1, 3, 0)],
12970        Some(vec![0]),
12971        &mut cx,
12972    );
12973}
12974
12975#[gpui::test]
12976async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12977    let (buffer_id, mut cx) = setup_indent_guides_editor(
12978        &"
12979    def m:
12980        a = 1
12981        pass"
12982            .unindent(),
12983        cx,
12984    )
12985    .await;
12986
12987    cx.update_editor(|editor, cx| {
12988        editor.change_selections(None, cx, |s| {
12989            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12990        });
12991    });
12992
12993    assert_indent_guides(
12994        0..3,
12995        vec![indent_guide(buffer_id, 1, 2, 0)],
12996        Some(vec![0]),
12997        &mut cx,
12998    );
12999}
13000
13001#[gpui::test]
13002fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13003    init_test(cx, |_| {});
13004
13005    let editor = cx.add_window(|cx| {
13006        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13007        build_editor(buffer, cx)
13008    });
13009
13010    let render_args = Arc::new(Mutex::new(None));
13011    let snapshot = editor
13012        .update(cx, |editor, cx| {
13013            let snapshot = editor.buffer().read(cx).snapshot(cx);
13014            let range =
13015                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13016
13017            struct RenderArgs {
13018                row: MultiBufferRow,
13019                folded: bool,
13020                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13021            }
13022
13023            let crease = Crease::new(
13024                range,
13025                FoldPlaceholder::test(),
13026                {
13027                    let toggle_callback = render_args.clone();
13028                    move |row, folded, callback, _cx| {
13029                        *toggle_callback.lock() = Some(RenderArgs {
13030                            row,
13031                            folded,
13032                            callback,
13033                        });
13034                        div()
13035                    }
13036                },
13037                |_row, _folded, _cx| div(),
13038            );
13039
13040            editor.insert_creases(Some(crease), cx);
13041            let snapshot = editor.snapshot(cx);
13042            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13043            snapshot
13044        })
13045        .unwrap();
13046
13047    let render_args = render_args.lock().take().unwrap();
13048    assert_eq!(render_args.row, MultiBufferRow(1));
13049    assert_eq!(render_args.folded, false);
13050    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13051
13052    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13053        .unwrap();
13054    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13055    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13056
13057    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13058        .unwrap();
13059    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13060    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13061}
13062
13063fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13064    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13065    point..point
13066}
13067
13068fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13069    let (text, ranges) = marked_text_ranges(marked_text, true);
13070    assert_eq!(view.text(cx), text);
13071    assert_eq!(
13072        view.selections.ranges(cx),
13073        ranges,
13074        "Assert selections are {}",
13075        marked_text
13076    );
13077}
13078
13079pub fn handle_signature_help_request(
13080    cx: &mut EditorLspTestContext,
13081    mocked_response: lsp::SignatureHelp,
13082) -> impl Future<Output = ()> {
13083    let mut request =
13084        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13085            let mocked_response = mocked_response.clone();
13086            async move { Ok(Some(mocked_response)) }
13087        });
13088
13089    async move {
13090        request.next().await;
13091    }
13092}
13093
13094/// Handle completion request passing a marked string specifying where the completion
13095/// should be triggered from using '|' character, what range should be replaced, and what completions
13096/// should be returned using '<' and '>' to delimit the range
13097pub fn handle_completion_request(
13098    cx: &mut EditorLspTestContext,
13099    marked_string: &str,
13100    completions: Vec<&'static str>,
13101    counter: Arc<AtomicUsize>,
13102) -> impl Future<Output = ()> {
13103    let complete_from_marker: TextRangeMarker = '|'.into();
13104    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13105    let (_, mut marked_ranges) = marked_text_ranges_by(
13106        marked_string,
13107        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13108    );
13109
13110    let complete_from_position =
13111        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13112    let replace_range =
13113        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13114
13115    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13116        let completions = completions.clone();
13117        counter.fetch_add(1, atomic::Ordering::Release);
13118        async move {
13119            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13120            assert_eq!(
13121                params.text_document_position.position,
13122                complete_from_position
13123            );
13124            Ok(Some(lsp::CompletionResponse::Array(
13125                completions
13126                    .iter()
13127                    .map(|completion_text| lsp::CompletionItem {
13128                        label: completion_text.to_string(),
13129                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13130                            range: replace_range,
13131                            new_text: completion_text.to_string(),
13132                        })),
13133                        ..Default::default()
13134                    })
13135                    .collect(),
13136            )))
13137        }
13138    });
13139
13140    async move {
13141        request.next().await;
13142    }
13143}
13144
13145fn handle_resolve_completion_request(
13146    cx: &mut EditorLspTestContext,
13147    edits: Option<Vec<(&'static str, &'static str)>>,
13148) -> impl Future<Output = ()> {
13149    let edits = edits.map(|edits| {
13150        edits
13151            .iter()
13152            .map(|(marked_string, new_text)| {
13153                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13154                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13155                lsp::TextEdit::new(replace_range, new_text.to_string())
13156            })
13157            .collect::<Vec<_>>()
13158    });
13159
13160    let mut request =
13161        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13162            let edits = edits.clone();
13163            async move {
13164                Ok(lsp::CompletionItem {
13165                    additional_text_edits: edits,
13166                    ..Default::default()
13167                })
13168            }
13169        });
13170
13171    async move {
13172        request.next().await;
13173    }
13174}
13175
13176pub(crate) fn update_test_language_settings(
13177    cx: &mut TestAppContext,
13178    f: impl Fn(&mut AllLanguageSettingsContent),
13179) {
13180    _ = cx.update(|cx| {
13181        SettingsStore::update_global(cx, |store, cx| {
13182            store.update_user_settings::<AllLanguageSettings>(cx, f);
13183        });
13184    });
13185}
13186
13187pub(crate) fn update_test_project_settings(
13188    cx: &mut TestAppContext,
13189    f: impl Fn(&mut ProjectSettings),
13190) {
13191    _ = cx.update(|cx| {
13192        SettingsStore::update_global(cx, |store, cx| {
13193            store.update_user_settings::<ProjectSettings>(cx, f);
13194        });
13195    });
13196}
13197
13198pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13199    _ = cx.update(|cx| {
13200        assets::Assets.load_test_fonts(cx);
13201        let store = SettingsStore::test(cx);
13202        cx.set_global(store);
13203        theme::init(theme::LoadThemes::JustBase, cx);
13204        release_channel::init(SemanticVersion::default(), cx);
13205        client::init_settings(cx);
13206        language::init(cx);
13207        Project::init_settings(cx);
13208        workspace::init_settings(cx);
13209        crate::init(cx);
13210    });
13211
13212    update_test_language_settings(cx, f);
13213}
13214
13215pub(crate) fn rust_lang() -> Arc<Language> {
13216    Arc::new(Language::new(
13217        LanguageConfig {
13218            name: "Rust".into(),
13219            matcher: LanguageMatcher {
13220                path_suffixes: vec!["rs".to_string()],
13221                ..Default::default()
13222            },
13223            ..Default::default()
13224        },
13225        Some(tree_sitter_rust::language()),
13226    ))
13227}
13228
13229#[track_caller]
13230fn assert_hunk_revert(
13231    not_reverted_text_with_selections: &str,
13232    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13233    expected_reverted_text_with_selections: &str,
13234    base_text: &str,
13235    cx: &mut EditorLspTestContext,
13236) {
13237    cx.set_state(not_reverted_text_with_selections);
13238    cx.update_editor(|editor, cx| {
13239        editor
13240            .buffer()
13241            .read(cx)
13242            .as_singleton()
13243            .unwrap()
13244            .update(cx, |buffer, cx| {
13245                buffer.set_diff_base(Some(base_text.into()), cx);
13246            });
13247    });
13248    cx.executor().run_until_parked();
13249
13250    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13251        let snapshot = editor.buffer().read(cx).snapshot(cx);
13252        let reverted_hunk_statuses = snapshot
13253            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13254            .map(|hunk| hunk_status(&hunk))
13255            .collect::<Vec<_>>();
13256
13257        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13258        reverted_hunk_statuses
13259    });
13260    cx.executor().run_until_parked();
13261    cx.assert_editor_state(expected_reverted_text_with_selections);
13262    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13263}