editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788            }],
 3789            Some(Autoscroll::fit()),
 3790            cx,
 3791        );
 3792        editor.change_selections(None, cx, |s| {
 3793            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3794        });
 3795        editor.move_line_down(&MoveLineDown, cx);
 3796    });
 3797}
 3798
 3799#[gpui::test]
 3800fn test_transpose(cx: &mut TestAppContext) {
 3801    init_test(cx, |_| {});
 3802
 3803    _ = cx.add_window(|cx| {
 3804        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3805        editor.set_style(EditorStyle::default(), cx);
 3806        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3807        editor.transpose(&Default::default(), cx);
 3808        assert_eq!(editor.text(cx), "bac");
 3809        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3810
 3811        editor.transpose(&Default::default(), cx);
 3812        assert_eq!(editor.text(cx), "bca");
 3813        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3814
 3815        editor.transpose(&Default::default(), cx);
 3816        assert_eq!(editor.text(cx), "bac");
 3817        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3818
 3819        editor
 3820    });
 3821
 3822    _ = cx.add_window(|cx| {
 3823        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3824        editor.set_style(EditorStyle::default(), cx);
 3825        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3826        editor.transpose(&Default::default(), cx);
 3827        assert_eq!(editor.text(cx), "acb\nde");
 3828        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3829
 3830        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3831        editor.transpose(&Default::default(), cx);
 3832        assert_eq!(editor.text(cx), "acbd\ne");
 3833        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3834
 3835        editor.transpose(&Default::default(), cx);
 3836        assert_eq!(editor.text(cx), "acbde\n");
 3837        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3838
 3839        editor.transpose(&Default::default(), cx);
 3840        assert_eq!(editor.text(cx), "acbd\ne");
 3841        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3842
 3843        editor
 3844    });
 3845
 3846    _ = cx.add_window(|cx| {
 3847        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3848        editor.set_style(EditorStyle::default(), cx);
 3849        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3850        editor.transpose(&Default::default(), cx);
 3851        assert_eq!(editor.text(cx), "bacd\ne");
 3852        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3853
 3854        editor.transpose(&Default::default(), cx);
 3855        assert_eq!(editor.text(cx), "bcade\n");
 3856        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3857
 3858        editor.transpose(&Default::default(), cx);
 3859        assert_eq!(editor.text(cx), "bcda\ne");
 3860        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3861
 3862        editor.transpose(&Default::default(), cx);
 3863        assert_eq!(editor.text(cx), "bcade\n");
 3864        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3865
 3866        editor.transpose(&Default::default(), cx);
 3867        assert_eq!(editor.text(cx), "bcaed\n");
 3868        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3869
 3870        editor
 3871    });
 3872
 3873    _ = cx.add_window(|cx| {
 3874        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3875        editor.set_style(EditorStyle::default(), cx);
 3876        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3877        editor.transpose(&Default::default(), cx);
 3878        assert_eq!(editor.text(cx), "🏀🍐✋");
 3879        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3880
 3881        editor.transpose(&Default::default(), cx);
 3882        assert_eq!(editor.text(cx), "🏀✋🍐");
 3883        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3884
 3885        editor.transpose(&Default::default(), cx);
 3886        assert_eq!(editor.text(cx), "🏀🍐✋");
 3887        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3888
 3889        editor
 3890    });
 3891}
 3892
 3893#[gpui::test]
 3894async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3895    init_test(cx, |_| {});
 3896
 3897    let mut cx = EditorTestContext::new(cx).await;
 3898
 3899    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3900    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3901    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3902
 3903    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3904    cx.set_state("two ˇfour ˇsix ˇ");
 3905    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3906    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3907
 3908    // Paste again but with only two cursors. Since the number of cursors doesn't
 3909    // match the number of slices in the clipboard, the entire clipboard text
 3910    // is pasted at each cursor.
 3911    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3912    cx.update_editor(|e, cx| {
 3913        e.handle_input("( ", cx);
 3914        e.paste(&Paste, cx);
 3915        e.handle_input(") ", cx);
 3916    });
 3917    cx.assert_editor_state(
 3918        &([
 3919            "( one✅ ",
 3920            "three ",
 3921            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3922            "three ",
 3923            "five ) ˇ",
 3924        ]
 3925        .join("\n")),
 3926    );
 3927
 3928    // Cut with three selections, one of which is full-line.
 3929    cx.set_state(indoc! {"
 3930        1«2ˇ»3
 3931        4ˇ567
 3932        «8ˇ»9"});
 3933    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3934    cx.assert_editor_state(indoc! {"
 3935        1ˇ3
 3936        ˇ9"});
 3937
 3938    // Paste with three selections, noticing how the copied selection that was full-line
 3939    // gets inserted before the second cursor.
 3940    cx.set_state(indoc! {"
 3941        1ˇ3
 3942 3943        «oˇ»ne"});
 3944    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3945    cx.assert_editor_state(indoc! {"
 3946        12ˇ3
 3947        4567
 3948 3949        8ˇne"});
 3950
 3951    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3952    cx.set_state(indoc! {"
 3953        The quick brown
 3954        fox juˇmps over
 3955        the lazy dog"});
 3956    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3957    assert_eq!(
 3958        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3959        Some("fox jumps over\n".to_owned())
 3960    );
 3961
 3962    // Paste with three selections, noticing how the copied full-line selection is inserted
 3963    // before the empty selections but replaces the selection that is non-empty.
 3964    cx.set_state(indoc! {"
 3965        Tˇhe quick brown
 3966        «foˇ»x jumps over
 3967        tˇhe lazy dog"});
 3968    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3969    cx.assert_editor_state(indoc! {"
 3970        fox jumps over
 3971        Tˇhe quick brown
 3972        fox jumps over
 3973        ˇx jumps over
 3974        fox jumps over
 3975        tˇhe lazy dog"});
 3976}
 3977
 3978#[gpui::test]
 3979async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3980    init_test(cx, |_| {});
 3981
 3982    let mut cx = EditorTestContext::new(cx).await;
 3983    let language = Arc::new(Language::new(
 3984        LanguageConfig::default(),
 3985        Some(tree_sitter_rust::language()),
 3986    ));
 3987    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3988
 3989    // Cut an indented block, without the leading whitespace.
 3990    cx.set_state(indoc! {"
 3991        const a: B = (
 3992            c(),
 3993            «d(
 3994                e,
 3995                f
 3996            )ˇ»
 3997        );
 3998    "});
 3999    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4000    cx.assert_editor_state(indoc! {"
 4001        const a: B = (
 4002            c(),
 4003            ˇ
 4004        );
 4005    "});
 4006
 4007    // Paste it at the same position.
 4008    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4009    cx.assert_editor_state(indoc! {"
 4010        const a: B = (
 4011            c(),
 4012            d(
 4013                e,
 4014                f
 4015 4016        );
 4017    "});
 4018
 4019    // Paste it at a line with a lower indent level.
 4020    cx.set_state(indoc! {"
 4021        ˇ
 4022        const a: B = (
 4023            c(),
 4024        );
 4025    "});
 4026    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4027    cx.assert_editor_state(indoc! {"
 4028        d(
 4029            e,
 4030            f
 4031 4032        const a: B = (
 4033            c(),
 4034        );
 4035    "});
 4036
 4037    // Cut an indented block, with the leading whitespace.
 4038    cx.set_state(indoc! {"
 4039        const a: B = (
 4040            c(),
 4041        «    d(
 4042                e,
 4043                f
 4044            )
 4045        ˇ»);
 4046    "});
 4047    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4048    cx.assert_editor_state(indoc! {"
 4049        const a: B = (
 4050            c(),
 4051        ˇ);
 4052    "});
 4053
 4054    // Paste it at the same position.
 4055    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4056    cx.assert_editor_state(indoc! {"
 4057        const a: B = (
 4058            c(),
 4059            d(
 4060                e,
 4061                f
 4062            )
 4063        ˇ);
 4064    "});
 4065
 4066    // Paste it at a line with a higher indent level.
 4067    cx.set_state(indoc! {"
 4068        const a: B = (
 4069            c(),
 4070            d(
 4071                e,
 4072 4073            )
 4074        );
 4075    "});
 4076    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4077    cx.assert_editor_state(indoc! {"
 4078        const a: B = (
 4079            c(),
 4080            d(
 4081                e,
 4082                f    d(
 4083                    e,
 4084                    f
 4085                )
 4086        ˇ
 4087            )
 4088        );
 4089    "});
 4090}
 4091
 4092#[gpui::test]
 4093fn test_select_all(cx: &mut TestAppContext) {
 4094    init_test(cx, |_| {});
 4095
 4096    let view = cx.add_window(|cx| {
 4097        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4098        build_editor(buffer, cx)
 4099    });
 4100    _ = view.update(cx, |view, cx| {
 4101        view.select_all(&SelectAll, cx);
 4102        assert_eq!(
 4103            view.selections.display_ranges(cx),
 4104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4105        );
 4106    });
 4107}
 4108
 4109#[gpui::test]
 4110fn test_select_line(cx: &mut TestAppContext) {
 4111    init_test(cx, |_| {});
 4112
 4113    let view = cx.add_window(|cx| {
 4114        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4115        build_editor(buffer, cx)
 4116    });
 4117    _ = view.update(cx, |view, cx| {
 4118        view.change_selections(None, cx, |s| {
 4119            s.select_display_ranges([
 4120                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4121                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4122                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4123                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4124            ])
 4125        });
 4126        view.select_line(&SelectLine, cx);
 4127        assert_eq!(
 4128            view.selections.display_ranges(cx),
 4129            vec![
 4130                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    _ = view.update(cx, |view, cx| {
 4137        view.select_line(&SelectLine, cx);
 4138        assert_eq!(
 4139            view.selections.display_ranges(cx),
 4140            vec![
 4141                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4142                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4143            ]
 4144        );
 4145    });
 4146
 4147    _ = view.update(cx, |view, cx| {
 4148        view.select_line(&SelectLine, cx);
 4149        assert_eq!(
 4150            view.selections.display_ranges(cx),
 4151            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4152        );
 4153    });
 4154}
 4155
 4156#[gpui::test]
 4157fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4158    init_test(cx, |_| {});
 4159
 4160    let view = cx.add_window(|cx| {
 4161        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4162        build_editor(buffer, cx)
 4163    });
 4164    _ = view.update(cx, |view, cx| {
 4165        view.fold_ranges(
 4166            vec![
 4167                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4168                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4169                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4170            ],
 4171            true,
 4172            cx,
 4173        );
 4174        view.change_selections(None, cx, |s| {
 4175            s.select_display_ranges([
 4176                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4177                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4178                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4179                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4180            ])
 4181        });
 4182        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4183    });
 4184
 4185    _ = view.update(cx, |view, cx| {
 4186        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4187        assert_eq!(
 4188            view.display_text(cx),
 4189            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4190        );
 4191        assert_eq!(
 4192            view.selections.display_ranges(cx),
 4193            [
 4194                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4195                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4196                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4197                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4198            ]
 4199        );
 4200    });
 4201
 4202    _ = view.update(cx, |view, cx| {
 4203        view.change_selections(None, cx, |s| {
 4204            s.select_display_ranges([
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4206            ])
 4207        });
 4208        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4209        assert_eq!(
 4210            view.display_text(cx),
 4211            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4212        );
 4213        assert_eq!(
 4214            view.selections.display_ranges(cx),
 4215            [
 4216                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4217                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4218                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4219                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4220                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4221                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4222                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4223                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4224            ]
 4225        );
 4226    });
 4227}
 4228
 4229#[gpui::test]
 4230async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    let mut cx = EditorTestContext::new(cx).await;
 4234
 4235    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4236    cx.set_state(indoc!(
 4237        r#"abc
 4238           defˇghi
 4239
 4240           jk
 4241           nlmo
 4242           "#
 4243    ));
 4244
 4245    cx.update_editor(|editor, cx| {
 4246        editor.add_selection_above(&Default::default(), cx);
 4247    });
 4248
 4249    cx.assert_editor_state(indoc!(
 4250        r#"abcˇ
 4251           defˇghi
 4252
 4253           jk
 4254           nlmo
 4255           "#
 4256    ));
 4257
 4258    cx.update_editor(|editor, cx| {
 4259        editor.add_selection_above(&Default::default(), cx);
 4260    });
 4261
 4262    cx.assert_editor_state(indoc!(
 4263        r#"abcˇ
 4264            defˇghi
 4265
 4266            jk
 4267            nlmo
 4268            "#
 4269    ));
 4270
 4271    cx.update_editor(|view, cx| {
 4272        view.add_selection_below(&Default::default(), cx);
 4273    });
 4274
 4275    cx.assert_editor_state(indoc!(
 4276        r#"abc
 4277           defˇghi
 4278
 4279           jk
 4280           nlmo
 4281           "#
 4282    ));
 4283
 4284    cx.update_editor(|view, cx| {
 4285        view.undo_selection(&Default::default(), cx);
 4286    });
 4287
 4288    cx.assert_editor_state(indoc!(
 4289        r#"abcˇ
 4290           defˇghi
 4291
 4292           jk
 4293           nlmo
 4294           "#
 4295    ));
 4296
 4297    cx.update_editor(|view, cx| {
 4298        view.redo_selection(&Default::default(), cx);
 4299    });
 4300
 4301    cx.assert_editor_state(indoc!(
 4302        r#"abc
 4303           defˇghi
 4304
 4305           jk
 4306           nlmo
 4307           "#
 4308    ));
 4309
 4310    cx.update_editor(|view, cx| {
 4311        view.add_selection_below(&Default::default(), cx);
 4312    });
 4313
 4314    cx.assert_editor_state(indoc!(
 4315        r#"abc
 4316           defˇghi
 4317
 4318           jk
 4319           nlmˇo
 4320           "#
 4321    ));
 4322
 4323    cx.update_editor(|view, cx| {
 4324        view.add_selection_below(&Default::default(), cx);
 4325    });
 4326
 4327    cx.assert_editor_state(indoc!(
 4328        r#"abc
 4329           defˇghi
 4330
 4331           jk
 4332           nlmˇo
 4333           "#
 4334    ));
 4335
 4336    // change selections
 4337    cx.set_state(indoc!(
 4338        r#"abc
 4339           def«ˇg»hi
 4340
 4341           jk
 4342           nlmo
 4343           "#
 4344    ));
 4345
 4346    cx.update_editor(|view, cx| {
 4347        view.add_selection_below(&Default::default(), cx);
 4348    });
 4349
 4350    cx.assert_editor_state(indoc!(
 4351        r#"abc
 4352           def«ˇg»hi
 4353
 4354           jk
 4355           nlm«ˇo»
 4356           "#
 4357    ));
 4358
 4359    cx.update_editor(|view, cx| {
 4360        view.add_selection_below(&Default::default(), cx);
 4361    });
 4362
 4363    cx.assert_editor_state(indoc!(
 4364        r#"abc
 4365           def«ˇg»hi
 4366
 4367           jk
 4368           nlm«ˇo»
 4369           "#
 4370    ));
 4371
 4372    cx.update_editor(|view, cx| {
 4373        view.add_selection_above(&Default::default(), cx);
 4374    });
 4375
 4376    cx.assert_editor_state(indoc!(
 4377        r#"abc
 4378           def«ˇg»hi
 4379
 4380           jk
 4381           nlmo
 4382           "#
 4383    ));
 4384
 4385    cx.update_editor(|view, cx| {
 4386        view.add_selection_above(&Default::default(), cx);
 4387    });
 4388
 4389    cx.assert_editor_state(indoc!(
 4390        r#"abc
 4391           def«ˇg»hi
 4392
 4393           jk
 4394           nlmo
 4395           "#
 4396    ));
 4397
 4398    // Change selections again
 4399    cx.set_state(indoc!(
 4400        r#"a«bc
 4401           defgˇ»hi
 4402
 4403           jk
 4404           nlmo
 4405           "#
 4406    ));
 4407
 4408    cx.update_editor(|view, cx| {
 4409        view.add_selection_below(&Default::default(), cx);
 4410    });
 4411
 4412    cx.assert_editor_state(indoc!(
 4413        r#"a«bcˇ»
 4414           d«efgˇ»hi
 4415
 4416           j«kˇ»
 4417           nlmo
 4418           "#
 4419    ));
 4420
 4421    cx.update_editor(|view, cx| {
 4422        view.add_selection_below(&Default::default(), cx);
 4423    });
 4424    cx.assert_editor_state(indoc!(
 4425        r#"a«bcˇ»
 4426           d«efgˇ»hi
 4427
 4428           j«kˇ»
 4429           n«lmoˇ»
 4430           "#
 4431    ));
 4432    cx.update_editor(|view, cx| {
 4433        view.add_selection_above(&Default::default(), cx);
 4434    });
 4435
 4436    cx.assert_editor_state(indoc!(
 4437        r#"a«bcˇ»
 4438           d«efgˇ»hi
 4439
 4440           j«kˇ»
 4441           nlmo
 4442           "#
 4443    ));
 4444
 4445    // Change selections again
 4446    cx.set_state(indoc!(
 4447        r#"abc
 4448           d«ˇefghi
 4449
 4450           jk
 4451           nlm»o
 4452           "#
 4453    ));
 4454
 4455    cx.update_editor(|view, cx| {
 4456        view.add_selection_above(&Default::default(), cx);
 4457    });
 4458
 4459    cx.assert_editor_state(indoc!(
 4460        r#"a«ˇbc»
 4461           d«ˇef»ghi
 4462
 4463           j«ˇk»
 4464           n«ˇlm»o
 4465           "#
 4466    ));
 4467
 4468    cx.update_editor(|view, cx| {
 4469        view.add_selection_below(&Default::default(), cx);
 4470    });
 4471
 4472    cx.assert_editor_state(indoc!(
 4473        r#"abc
 4474           d«ˇef»ghi
 4475
 4476           j«ˇk»
 4477           n«ˇlm»o
 4478           "#
 4479    ));
 4480}
 4481
 4482#[gpui::test]
 4483async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4484    init_test(cx, |_| {});
 4485
 4486    let mut cx = EditorTestContext::new(cx).await;
 4487    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4488
 4489    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4490        .unwrap();
 4491    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4492
 4493    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4494        .unwrap();
 4495    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4496
 4497    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4498    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4499
 4500    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4501    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4502
 4503    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4504        .unwrap();
 4505    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4506
 4507    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4508        .unwrap();
 4509    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4510}
 4511
 4512#[gpui::test]
 4513async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4514    init_test(cx, |_| {});
 4515
 4516    let mut cx = EditorTestContext::new(cx).await;
 4517    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4518
 4519    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4520        .unwrap();
 4521    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4522}
 4523
 4524#[gpui::test]
 4525async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4526    init_test(cx, |_| {});
 4527
 4528    let mut cx = EditorTestContext::new(cx).await;
 4529    cx.set_state(
 4530        r#"let foo = 2;
 4531lˇet foo = 2;
 4532let fooˇ = 2;
 4533let foo = 2;
 4534let foo = ˇ2;"#,
 4535    );
 4536
 4537    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4538        .unwrap();
 4539    cx.assert_editor_state(
 4540        r#"let foo = 2;
 4541«letˇ» foo = 2;
 4542let «fooˇ» = 2;
 4543let foo = 2;
 4544let foo = «2ˇ»;"#,
 4545    );
 4546
 4547    // noop for multiple selections with different contents
 4548    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4549        .unwrap();
 4550    cx.assert_editor_state(
 4551        r#"let foo = 2;
 4552«letˇ» foo = 2;
 4553let «fooˇ» = 2;
 4554let foo = 2;
 4555let foo = «2ˇ»;"#,
 4556    );
 4557}
 4558
 4559#[gpui::test]
 4560async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4561    init_test(cx, |_| {});
 4562
 4563    let mut cx = EditorTestContext::new_multibuffer(
 4564        cx,
 4565        [
 4566            &indoc! {
 4567                "aaa\n«bbb\nccc\n»ddd"
 4568            },
 4569            &indoc! {
 4570                "aaa\n«bbb\nccc\n»ddd"
 4571            },
 4572        ],
 4573    );
 4574
 4575    cx.assert_editor_state(indoc! {"
 4576        ˇbbb
 4577        ccc
 4578
 4579        bbb
 4580        ccc
 4581        "});
 4582    cx.dispatch_action(SelectPrevious::default());
 4583    cx.assert_editor_state(indoc! {"
 4584                «bbbˇ»
 4585                ccc
 4586
 4587                bbb
 4588                ccc
 4589                "});
 4590    cx.dispatch_action(SelectPrevious::default());
 4591    cx.assert_editor_state(indoc! {"
 4592                «bbbˇ»
 4593                ccc
 4594
 4595                «bbbˇ»
 4596                ccc
 4597                "});
 4598}
 4599
 4600#[gpui::test]
 4601async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4602    init_test(cx, |_| {});
 4603
 4604    let mut cx = EditorTestContext::new(cx).await;
 4605    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4606
 4607    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4608        .unwrap();
 4609    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4610
 4611    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4612        .unwrap();
 4613    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4614
 4615    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4616    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4617
 4618    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4619    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4620
 4621    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4622        .unwrap();
 4623    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4624
 4625    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4626        .unwrap();
 4627    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4628
 4629    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4630        .unwrap();
 4631    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4632}
 4633
 4634#[gpui::test]
 4635async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4636    init_test(cx, |_| {});
 4637
 4638    let mut cx = EditorTestContext::new(cx).await;
 4639    cx.set_state(
 4640        r#"let foo = 2;
 4641lˇet foo = 2;
 4642let fooˇ = 2;
 4643let foo = 2;
 4644let foo = ˇ2;"#,
 4645    );
 4646
 4647    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4648        .unwrap();
 4649    cx.assert_editor_state(
 4650        r#"let foo = 2;
 4651«letˇ» foo = 2;
 4652let «fooˇ» = 2;
 4653let foo = 2;
 4654let foo = «2ˇ»;"#,
 4655    );
 4656
 4657    // noop for multiple selections with different contents
 4658    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4659        .unwrap();
 4660    cx.assert_editor_state(
 4661        r#"let foo = 2;
 4662«letˇ» foo = 2;
 4663let «fooˇ» = 2;
 4664let foo = 2;
 4665let foo = «2ˇ»;"#,
 4666    );
 4667}
 4668
 4669#[gpui::test]
 4670async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4671    init_test(cx, |_| {});
 4672
 4673    let mut cx = EditorTestContext::new(cx).await;
 4674    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4675
 4676    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4677        .unwrap();
 4678    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4679
 4680    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4681        .unwrap();
 4682    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4683
 4684    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4685    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4686
 4687    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4688    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4689
 4690    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4691        .unwrap();
 4692    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4693
 4694    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4695        .unwrap();
 4696    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4697}
 4698
 4699#[gpui::test]
 4700async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4701    init_test(cx, |_| {});
 4702
 4703    let language = Arc::new(Language::new(
 4704        LanguageConfig::default(),
 4705        Some(tree_sitter_rust::language()),
 4706    ));
 4707
 4708    let text = r#"
 4709        use mod1::mod2::{mod3, mod4};
 4710
 4711        fn fn_1(param1: bool, param2: &str) {
 4712            let var1 = "text";
 4713        }
 4714    "#
 4715    .unindent();
 4716
 4717    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4718    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4719    let (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, cx| {
 6257        let mut worktrees = project.worktrees(cx).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::SelectedFormatter::List(
 6563            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6564        ))
 6565    });
 6566
 6567    let fs = FakeFs::new(cx.executor());
 6568    fs.insert_file("/file.rs", Default::default()).await;
 6569
 6570    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6571
 6572    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6573    language_registry.add(Arc::new(Language::new(
 6574        LanguageConfig {
 6575            name: "Rust".into(),
 6576            matcher: LanguageMatcher {
 6577                path_suffixes: vec!["rs".to_string()],
 6578                ..Default::default()
 6579            },
 6580            ..LanguageConfig::default()
 6581        },
 6582        Some(tree_sitter_rust::language()),
 6583    )));
 6584    update_test_language_settings(cx, |settings| {
 6585        // Enable Prettier formatting for the same buffer, and ensure
 6586        // LSP is called instead of Prettier.
 6587        settings.defaults.prettier = Some(PrettierSettings {
 6588            allowed: true,
 6589            ..PrettierSettings::default()
 6590        });
 6591    });
 6592    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6593        "Rust",
 6594        FakeLspAdapter {
 6595            capabilities: lsp::ServerCapabilities {
 6596                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6597                ..Default::default()
 6598            },
 6599            ..Default::default()
 6600        },
 6601    );
 6602
 6603    let buffer = project
 6604        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6605        .await
 6606        .unwrap();
 6607
 6608    cx.executor().start_waiting();
 6609    let fake_server = fake_servers.next().await.unwrap();
 6610
 6611    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6612    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6613    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6614
 6615    let format = editor
 6616        .update(cx, |editor, cx| {
 6617            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6618        })
 6619        .unwrap();
 6620    fake_server
 6621        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6622            assert_eq!(
 6623                params.text_document.uri,
 6624                lsp::Url::from_file_path("/file.rs").unwrap()
 6625            );
 6626            assert_eq!(params.options.tab_size, 4);
 6627            Ok(Some(vec![lsp::TextEdit::new(
 6628                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6629                ", ".to_string(),
 6630            )]))
 6631        })
 6632        .next()
 6633        .await;
 6634    cx.executor().start_waiting();
 6635    format.await;
 6636    assert_eq!(
 6637        editor.update(cx, |editor, cx| editor.text(cx)),
 6638        "one, two\nthree\n"
 6639    );
 6640
 6641    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6642    // Ensure we don't lock if formatting hangs.
 6643    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6644        assert_eq!(
 6645            params.text_document.uri,
 6646            lsp::Url::from_file_path("/file.rs").unwrap()
 6647        );
 6648        futures::future::pending::<()>().await;
 6649        unreachable!()
 6650    });
 6651    let format = editor
 6652        .update(cx, |editor, cx| {
 6653            editor.perform_format(project, FormatTrigger::Manual, cx)
 6654        })
 6655        .unwrap();
 6656    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6657    cx.executor().start_waiting();
 6658    format.await;
 6659    assert_eq!(
 6660        editor.update(cx, |editor, cx| editor.text(cx)),
 6661        "one\ntwo\nthree\n"
 6662    );
 6663}
 6664
 6665#[gpui::test]
 6666async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6667    init_test(cx, |_| {});
 6668
 6669    let mut cx = EditorLspTestContext::new_rust(
 6670        lsp::ServerCapabilities {
 6671            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6672            ..Default::default()
 6673        },
 6674        cx,
 6675    )
 6676    .await;
 6677
 6678    cx.set_state(indoc! {"
 6679        one.twoˇ
 6680    "});
 6681
 6682    // The format request takes a long time. When it completes, it inserts
 6683    // a newline and an indent before the `.`
 6684    cx.lsp
 6685        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6686            let executor = cx.background_executor().clone();
 6687            async move {
 6688                executor.timer(Duration::from_millis(100)).await;
 6689                Ok(Some(vec![lsp::TextEdit {
 6690                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6691                    new_text: "\n    ".into(),
 6692                }]))
 6693            }
 6694        });
 6695
 6696    // Submit a format request.
 6697    let format_1 = cx
 6698        .update_editor(|editor, cx| editor.format(&Format, cx))
 6699        .unwrap();
 6700    cx.executor().run_until_parked();
 6701
 6702    // Submit a second format request.
 6703    let format_2 = cx
 6704        .update_editor(|editor, cx| editor.format(&Format, cx))
 6705        .unwrap();
 6706    cx.executor().run_until_parked();
 6707
 6708    // Wait for both format requests to complete
 6709    cx.executor().advance_clock(Duration::from_millis(200));
 6710    cx.executor().start_waiting();
 6711    format_1.await.unwrap();
 6712    cx.executor().start_waiting();
 6713    format_2.await.unwrap();
 6714
 6715    // The formatting edits only happens once.
 6716    cx.assert_editor_state(indoc! {"
 6717        one
 6718            .twoˇ
 6719    "});
 6720}
 6721
 6722#[gpui::test]
 6723async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6724    init_test(cx, |settings| {
 6725        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6726    });
 6727
 6728    let mut cx = EditorLspTestContext::new_rust(
 6729        lsp::ServerCapabilities {
 6730            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6731            ..Default::default()
 6732        },
 6733        cx,
 6734    )
 6735    .await;
 6736
 6737    // Set up a buffer white some trailing whitespace and no trailing newline.
 6738    cx.set_state(
 6739        &[
 6740            "one ",   //
 6741            "twoˇ",   //
 6742            "three ", //
 6743            "four",   //
 6744        ]
 6745        .join("\n"),
 6746    );
 6747
 6748    // Submit a format request.
 6749    let format = cx
 6750        .update_editor(|editor, cx| editor.format(&Format, cx))
 6751        .unwrap();
 6752
 6753    // Record which buffer changes have been sent to the language server
 6754    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6755    cx.lsp
 6756        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6757            let buffer_changes = buffer_changes.clone();
 6758            move |params, _| {
 6759                buffer_changes.lock().extend(
 6760                    params
 6761                        .content_changes
 6762                        .into_iter()
 6763                        .map(|e| (e.range.unwrap(), e.text)),
 6764                );
 6765            }
 6766        });
 6767
 6768    // Handle formatting requests to the language server.
 6769    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6770        let buffer_changes = buffer_changes.clone();
 6771        move |_, _| {
 6772            // When formatting is requested, trailing whitespace has already been stripped,
 6773            // and the trailing newline has already been added.
 6774            assert_eq!(
 6775                &buffer_changes.lock()[1..],
 6776                &[
 6777                    (
 6778                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6779                        "".into()
 6780                    ),
 6781                    (
 6782                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6783                        "".into()
 6784                    ),
 6785                    (
 6786                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6787                        "\n".into()
 6788                    ),
 6789                ]
 6790            );
 6791
 6792            // Insert blank lines between each line of the buffer.
 6793            async move {
 6794                Ok(Some(vec![
 6795                    lsp::TextEdit {
 6796                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6797                        new_text: "\n".into(),
 6798                    },
 6799                    lsp::TextEdit {
 6800                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6801                        new_text: "\n".into(),
 6802                    },
 6803                ]))
 6804            }
 6805        }
 6806    });
 6807
 6808    // After formatting the buffer, the trailing whitespace is stripped,
 6809    // a newline is appended, and the edits provided by the language server
 6810    // have been applied.
 6811    format.await.unwrap();
 6812    cx.assert_editor_state(
 6813        &[
 6814            "one",   //
 6815            "",      //
 6816            "twoˇ",  //
 6817            "",      //
 6818            "three", //
 6819            "four",  //
 6820            "",      //
 6821        ]
 6822        .join("\n"),
 6823    );
 6824
 6825    // Undoing the formatting undoes the trailing whitespace removal, the
 6826    // trailing newline, and the LSP edits.
 6827    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6828    cx.assert_editor_state(
 6829        &[
 6830            "one ",   //
 6831            "twoˇ",   //
 6832            "three ", //
 6833            "four",   //
 6834        ]
 6835        .join("\n"),
 6836    );
 6837}
 6838
 6839#[gpui::test]
 6840async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6841    cx: &mut gpui::TestAppContext,
 6842) {
 6843    init_test(cx, |_| {});
 6844
 6845    cx.update(|cx| {
 6846        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6847            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6848                settings.auto_signature_help = Some(true);
 6849            });
 6850        });
 6851    });
 6852
 6853    let mut cx = EditorLspTestContext::new_rust(
 6854        lsp::ServerCapabilities {
 6855            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6856                ..Default::default()
 6857            }),
 6858            ..Default::default()
 6859        },
 6860        cx,
 6861    )
 6862    .await;
 6863
 6864    let language = Language::new(
 6865        LanguageConfig {
 6866            name: "Rust".into(),
 6867            brackets: BracketPairConfig {
 6868                pairs: vec![
 6869                    BracketPair {
 6870                        start: "{".to_string(),
 6871                        end: "}".to_string(),
 6872                        close: true,
 6873                        surround: true,
 6874                        newline: true,
 6875                    },
 6876                    BracketPair {
 6877                        start: "(".to_string(),
 6878                        end: ")".to_string(),
 6879                        close: true,
 6880                        surround: true,
 6881                        newline: true,
 6882                    },
 6883                    BracketPair {
 6884                        start: "/*".to_string(),
 6885                        end: " */".to_string(),
 6886                        close: true,
 6887                        surround: true,
 6888                        newline: true,
 6889                    },
 6890                    BracketPair {
 6891                        start: "[".to_string(),
 6892                        end: "]".to_string(),
 6893                        close: false,
 6894                        surround: false,
 6895                        newline: true,
 6896                    },
 6897                    BracketPair {
 6898                        start: "\"".to_string(),
 6899                        end: "\"".to_string(),
 6900                        close: true,
 6901                        surround: true,
 6902                        newline: false,
 6903                    },
 6904                    BracketPair {
 6905                        start: "<".to_string(),
 6906                        end: ">".to_string(),
 6907                        close: false,
 6908                        surround: true,
 6909                        newline: true,
 6910                    },
 6911                ],
 6912                ..Default::default()
 6913            },
 6914            autoclose_before: "})]".to_string(),
 6915            ..Default::default()
 6916        },
 6917        Some(tree_sitter_rust::language()),
 6918    );
 6919    let language = Arc::new(language);
 6920
 6921    cx.language_registry().add(language.clone());
 6922    cx.update_buffer(|buffer, cx| {
 6923        buffer.set_language(Some(language), cx);
 6924    });
 6925
 6926    cx.set_state(
 6927        &r#"
 6928            fn main() {
 6929                sampleˇ
 6930            }
 6931        "#
 6932        .unindent(),
 6933    );
 6934
 6935    cx.update_editor(|view, cx| {
 6936        view.handle_input("(", cx);
 6937    });
 6938    cx.assert_editor_state(
 6939        &"
 6940            fn main() {
 6941                sample(ˇ)
 6942            }
 6943        "
 6944        .unindent(),
 6945    );
 6946
 6947    let mocked_response = lsp::SignatureHelp {
 6948        signatures: vec![lsp::SignatureInformation {
 6949            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6950            documentation: None,
 6951            parameters: Some(vec![
 6952                lsp::ParameterInformation {
 6953                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6954                    documentation: None,
 6955                },
 6956                lsp::ParameterInformation {
 6957                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6958                    documentation: None,
 6959                },
 6960            ]),
 6961            active_parameter: None,
 6962        }],
 6963        active_signature: Some(0),
 6964        active_parameter: Some(0),
 6965    };
 6966    handle_signature_help_request(&mut cx, mocked_response).await;
 6967
 6968    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 6969        .await;
 6970
 6971    cx.editor(|editor, _| {
 6972        let signature_help_state = editor.signature_help_state.popover().cloned();
 6973        assert!(signature_help_state.is_some());
 6974        let ParsedMarkdown {
 6975            text, highlights, ..
 6976        } = signature_help_state.unwrap().parsed_content;
 6977        assert_eq!(text, "param1: u8, param2: u8");
 6978        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 6979    });
 6980}
 6981
 6982#[gpui::test]
 6983async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 6984    init_test(cx, |_| {});
 6985
 6986    cx.update(|cx| {
 6987        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6988            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6989                settings.auto_signature_help = Some(false);
 6990                settings.show_signature_help_after_edits = Some(false);
 6991            });
 6992        });
 6993    });
 6994
 6995    let mut cx = EditorLspTestContext::new_rust(
 6996        lsp::ServerCapabilities {
 6997            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6998                ..Default::default()
 6999            }),
 7000            ..Default::default()
 7001        },
 7002        cx,
 7003    )
 7004    .await;
 7005
 7006    let language = Language::new(
 7007        LanguageConfig {
 7008            name: "Rust".into(),
 7009            brackets: BracketPairConfig {
 7010                pairs: vec![
 7011                    BracketPair {
 7012                        start: "{".to_string(),
 7013                        end: "}".to_string(),
 7014                        close: true,
 7015                        surround: true,
 7016                        newline: true,
 7017                    },
 7018                    BracketPair {
 7019                        start: "(".to_string(),
 7020                        end: ")".to_string(),
 7021                        close: true,
 7022                        surround: true,
 7023                        newline: true,
 7024                    },
 7025                    BracketPair {
 7026                        start: "/*".to_string(),
 7027                        end: " */".to_string(),
 7028                        close: true,
 7029                        surround: true,
 7030                        newline: true,
 7031                    },
 7032                    BracketPair {
 7033                        start: "[".to_string(),
 7034                        end: "]".to_string(),
 7035                        close: false,
 7036                        surround: false,
 7037                        newline: true,
 7038                    },
 7039                    BracketPair {
 7040                        start: "\"".to_string(),
 7041                        end: "\"".to_string(),
 7042                        close: true,
 7043                        surround: true,
 7044                        newline: false,
 7045                    },
 7046                    BracketPair {
 7047                        start: "<".to_string(),
 7048                        end: ">".to_string(),
 7049                        close: false,
 7050                        surround: true,
 7051                        newline: true,
 7052                    },
 7053                ],
 7054                ..Default::default()
 7055            },
 7056            autoclose_before: "})]".to_string(),
 7057            ..Default::default()
 7058        },
 7059        Some(tree_sitter_rust::language()),
 7060    );
 7061    let language = Arc::new(language);
 7062
 7063    cx.language_registry().add(language.clone());
 7064    cx.update_buffer(|buffer, cx| {
 7065        buffer.set_language(Some(language), cx);
 7066    });
 7067
 7068    // Ensure that signature_help is not called when no signature help is enabled.
 7069    cx.set_state(
 7070        &r#"
 7071            fn main() {
 7072                sampleˇ
 7073            }
 7074        "#
 7075        .unindent(),
 7076    );
 7077    cx.update_editor(|view, cx| {
 7078        view.handle_input("(", cx);
 7079    });
 7080    cx.assert_editor_state(
 7081        &"
 7082            fn main() {
 7083                sample(ˇ)
 7084            }
 7085        "
 7086        .unindent(),
 7087    );
 7088    cx.editor(|editor, _| {
 7089        assert!(editor.signature_help_state.task().is_none());
 7090    });
 7091
 7092    let mocked_response = lsp::SignatureHelp {
 7093        signatures: vec![lsp::SignatureInformation {
 7094            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7095            documentation: None,
 7096            parameters: Some(vec![
 7097                lsp::ParameterInformation {
 7098                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7099                    documentation: None,
 7100                },
 7101                lsp::ParameterInformation {
 7102                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7103                    documentation: None,
 7104                },
 7105            ]),
 7106            active_parameter: None,
 7107        }],
 7108        active_signature: Some(0),
 7109        active_parameter: Some(0),
 7110    };
 7111
 7112    // Ensure that signature_help is called when enabled afte edits
 7113    cx.update(|cx| {
 7114        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7115            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7116                settings.auto_signature_help = Some(false);
 7117                settings.show_signature_help_after_edits = Some(true);
 7118            });
 7119        });
 7120    });
 7121    cx.set_state(
 7122        &r#"
 7123            fn main() {
 7124                sampleˇ
 7125            }
 7126        "#
 7127        .unindent(),
 7128    );
 7129    cx.update_editor(|view, cx| {
 7130        view.handle_input("(", cx);
 7131    });
 7132    cx.assert_editor_state(
 7133        &"
 7134            fn main() {
 7135                sample(ˇ)
 7136            }
 7137        "
 7138        .unindent(),
 7139    );
 7140    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7141    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7142        .await;
 7143    cx.update_editor(|editor, _| {
 7144        let signature_help_state = editor.signature_help_state.popover().cloned();
 7145        assert!(signature_help_state.is_some());
 7146        let ParsedMarkdown {
 7147            text, highlights, ..
 7148        } = signature_help_state.unwrap().parsed_content;
 7149        assert_eq!(text, "param1: u8, param2: u8");
 7150        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7151        editor.signature_help_state = SignatureHelpState::default();
 7152    });
 7153
 7154    // Ensure that signature_help is called when auto signature help override is enabled
 7155    cx.update(|cx| {
 7156        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7157            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7158                settings.auto_signature_help = Some(true);
 7159                settings.show_signature_help_after_edits = Some(false);
 7160            });
 7161        });
 7162    });
 7163    cx.set_state(
 7164        &r#"
 7165            fn main() {
 7166                sampleˇ
 7167            }
 7168        "#
 7169        .unindent(),
 7170    );
 7171    cx.update_editor(|view, cx| {
 7172        view.handle_input("(", cx);
 7173    });
 7174    cx.assert_editor_state(
 7175        &"
 7176            fn main() {
 7177                sample(ˇ)
 7178            }
 7179        "
 7180        .unindent(),
 7181    );
 7182    handle_signature_help_request(&mut cx, mocked_response).await;
 7183    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7184        .await;
 7185    cx.editor(|editor, _| {
 7186        let signature_help_state = editor.signature_help_state.popover().cloned();
 7187        assert!(signature_help_state.is_some());
 7188        let ParsedMarkdown {
 7189            text, highlights, ..
 7190        } = signature_help_state.unwrap().parsed_content;
 7191        assert_eq!(text, "param1: u8, param2: u8");
 7192        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7193    });
 7194}
 7195
 7196#[gpui::test]
 7197async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7198    init_test(cx, |_| {});
 7199    cx.update(|cx| {
 7200        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7201            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7202                settings.auto_signature_help = Some(true);
 7203            });
 7204        });
 7205    });
 7206
 7207    let mut cx = EditorLspTestContext::new_rust(
 7208        lsp::ServerCapabilities {
 7209            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7210                ..Default::default()
 7211            }),
 7212            ..Default::default()
 7213        },
 7214        cx,
 7215    )
 7216    .await;
 7217
 7218    // A test that directly calls `show_signature_help`
 7219    cx.update_editor(|editor, cx| {
 7220        editor.show_signature_help(&ShowSignatureHelp, cx);
 7221    });
 7222
 7223    let mocked_response = lsp::SignatureHelp {
 7224        signatures: vec![lsp::SignatureInformation {
 7225            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7226            documentation: None,
 7227            parameters: Some(vec![
 7228                lsp::ParameterInformation {
 7229                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7230                    documentation: None,
 7231                },
 7232                lsp::ParameterInformation {
 7233                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7234                    documentation: None,
 7235                },
 7236            ]),
 7237            active_parameter: None,
 7238        }],
 7239        active_signature: Some(0),
 7240        active_parameter: Some(0),
 7241    };
 7242    handle_signature_help_request(&mut cx, mocked_response).await;
 7243
 7244    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7245        .await;
 7246
 7247    cx.editor(|editor, _| {
 7248        let signature_help_state = editor.signature_help_state.popover().cloned();
 7249        assert!(signature_help_state.is_some());
 7250        let ParsedMarkdown {
 7251            text, highlights, ..
 7252        } = signature_help_state.unwrap().parsed_content;
 7253        assert_eq!(text, "param1: u8, param2: u8");
 7254        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7255    });
 7256
 7257    // When exiting outside from inside the brackets, `signature_help` is closed.
 7258    cx.set_state(indoc! {"
 7259        fn main() {
 7260            sample(ˇ);
 7261        }
 7262
 7263        fn sample(param1: u8, param2: u8) {}
 7264    "});
 7265
 7266    cx.update_editor(|editor, cx| {
 7267        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7268    });
 7269
 7270    let mocked_response = lsp::SignatureHelp {
 7271        signatures: Vec::new(),
 7272        active_signature: None,
 7273        active_parameter: None,
 7274    };
 7275    handle_signature_help_request(&mut cx, mocked_response).await;
 7276
 7277    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7278        .await;
 7279
 7280    cx.editor(|editor, _| {
 7281        assert!(!editor.signature_help_state.is_shown());
 7282    });
 7283
 7284    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7285    cx.set_state(indoc! {"
 7286        fn main() {
 7287            sample(ˇ);
 7288        }
 7289
 7290        fn sample(param1: u8, param2: u8) {}
 7291    "});
 7292
 7293    let mocked_response = lsp::SignatureHelp {
 7294        signatures: vec![lsp::SignatureInformation {
 7295            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7296            documentation: None,
 7297            parameters: Some(vec![
 7298                lsp::ParameterInformation {
 7299                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7300                    documentation: None,
 7301                },
 7302                lsp::ParameterInformation {
 7303                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7304                    documentation: None,
 7305                },
 7306            ]),
 7307            active_parameter: None,
 7308        }],
 7309        active_signature: Some(0),
 7310        active_parameter: Some(0),
 7311    };
 7312    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7313    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7314        .await;
 7315    cx.editor(|editor, _| {
 7316        assert!(editor.signature_help_state.is_shown());
 7317    });
 7318
 7319    // Restore the popover with more parameter input
 7320    cx.set_state(indoc! {"
 7321        fn main() {
 7322            sample(param1, param2ˇ);
 7323        }
 7324
 7325        fn sample(param1: u8, param2: u8) {}
 7326    "});
 7327
 7328    let mocked_response = lsp::SignatureHelp {
 7329        signatures: vec![lsp::SignatureInformation {
 7330            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7331            documentation: None,
 7332            parameters: Some(vec![
 7333                lsp::ParameterInformation {
 7334                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7335                    documentation: None,
 7336                },
 7337                lsp::ParameterInformation {
 7338                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7339                    documentation: None,
 7340                },
 7341            ]),
 7342            active_parameter: None,
 7343        }],
 7344        active_signature: Some(0),
 7345        active_parameter: Some(1),
 7346    };
 7347    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7348    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7349        .await;
 7350
 7351    // When selecting a range, the popover is gone.
 7352    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7353    cx.update_editor(|editor, cx| {
 7354        editor.change_selections(None, cx, |s| {
 7355            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7356        })
 7357    });
 7358    cx.assert_editor_state(indoc! {"
 7359        fn main() {
 7360            sample(param1, «ˇparam2»);
 7361        }
 7362
 7363        fn sample(param1: u8, param2: u8) {}
 7364    "});
 7365    cx.editor(|editor, _| {
 7366        assert!(!editor.signature_help_state.is_shown());
 7367    });
 7368
 7369    // When unselecting again, the popover is back if within the brackets.
 7370    cx.update_editor(|editor, cx| {
 7371        editor.change_selections(None, cx, |s| {
 7372            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7373        })
 7374    });
 7375    cx.assert_editor_state(indoc! {"
 7376        fn main() {
 7377            sample(param1, ˇparam2);
 7378        }
 7379
 7380        fn sample(param1: u8, param2: u8) {}
 7381    "});
 7382    handle_signature_help_request(&mut cx, mocked_response).await;
 7383    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7384        .await;
 7385    cx.editor(|editor, _| {
 7386        assert!(editor.signature_help_state.is_shown());
 7387    });
 7388
 7389    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7390    cx.update_editor(|editor, cx| {
 7391        editor.change_selections(None, cx, |s| {
 7392            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7393            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7394        })
 7395    });
 7396    cx.assert_editor_state(indoc! {"
 7397        fn main() {
 7398            sample(param1, ˇparam2);
 7399        }
 7400
 7401        fn sample(param1: u8, param2: u8) {}
 7402    "});
 7403
 7404    let mocked_response = lsp::SignatureHelp {
 7405        signatures: vec![lsp::SignatureInformation {
 7406            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7407            documentation: None,
 7408            parameters: Some(vec![
 7409                lsp::ParameterInformation {
 7410                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7411                    documentation: None,
 7412                },
 7413                lsp::ParameterInformation {
 7414                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7415                    documentation: None,
 7416                },
 7417            ]),
 7418            active_parameter: None,
 7419        }],
 7420        active_signature: Some(0),
 7421        active_parameter: Some(1),
 7422    };
 7423    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7424    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7425        .await;
 7426    cx.update_editor(|editor, cx| {
 7427        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7428    });
 7429    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7430        .await;
 7431    cx.update_editor(|editor, cx| {
 7432        editor.change_selections(None, cx, |s| {
 7433            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7434        })
 7435    });
 7436    cx.assert_editor_state(indoc! {"
 7437        fn main() {
 7438            sample(param1, «ˇparam2»);
 7439        }
 7440
 7441        fn sample(param1: u8, param2: u8) {}
 7442    "});
 7443    cx.update_editor(|editor, cx| {
 7444        editor.change_selections(None, cx, |s| {
 7445            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7446        })
 7447    });
 7448    cx.assert_editor_state(indoc! {"
 7449        fn main() {
 7450            sample(param1, ˇparam2);
 7451        }
 7452
 7453        fn sample(param1: u8, param2: u8) {}
 7454    "});
 7455    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7456        .await;
 7457}
 7458
 7459#[gpui::test]
 7460async fn test_completion(cx: &mut gpui::TestAppContext) {
 7461    init_test(cx, |_| {});
 7462
 7463    let mut cx = EditorLspTestContext::new_rust(
 7464        lsp::ServerCapabilities {
 7465            completion_provider: Some(lsp::CompletionOptions {
 7466                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7467                resolve_provider: Some(true),
 7468                ..Default::default()
 7469            }),
 7470            ..Default::default()
 7471        },
 7472        cx,
 7473    )
 7474    .await;
 7475    let counter = Arc::new(AtomicUsize::new(0));
 7476
 7477    cx.set_state(indoc! {"
 7478        oneˇ
 7479        two
 7480        three
 7481    "});
 7482    cx.simulate_keystroke(".");
 7483    handle_completion_request(
 7484        &mut cx,
 7485        indoc! {"
 7486            one.|<>
 7487            two
 7488            three
 7489        "},
 7490        vec!["first_completion", "second_completion"],
 7491        counter.clone(),
 7492    )
 7493    .await;
 7494    cx.condition(|editor, _| editor.context_menu_visible())
 7495        .await;
 7496    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7497
 7498    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7499        editor.context_menu_next(&Default::default(), cx);
 7500        editor
 7501            .confirm_completion(&ConfirmCompletion::default(), cx)
 7502            .unwrap()
 7503    });
 7504    cx.assert_editor_state(indoc! {"
 7505        one.second_completionˇ
 7506        two
 7507        three
 7508    "});
 7509
 7510    handle_resolve_completion_request(
 7511        &mut cx,
 7512        Some(vec![
 7513            (
 7514                //This overlaps with the primary completion edit which is
 7515                //misbehavior from the LSP spec, test that we filter it out
 7516                indoc! {"
 7517                    one.second_ˇcompletion
 7518                    two
 7519                    threeˇ
 7520                "},
 7521                "overlapping additional edit",
 7522            ),
 7523            (
 7524                indoc! {"
 7525                    one.second_completion
 7526                    two
 7527                    threeˇ
 7528                "},
 7529                "\nadditional edit",
 7530            ),
 7531        ]),
 7532    )
 7533    .await;
 7534    apply_additional_edits.await.unwrap();
 7535    cx.assert_editor_state(indoc! {"
 7536        one.second_completionˇ
 7537        two
 7538        three
 7539        additional edit
 7540    "});
 7541
 7542    cx.set_state(indoc! {"
 7543        one.second_completion
 7544        twoˇ
 7545        threeˇ
 7546        additional edit
 7547    "});
 7548    cx.simulate_keystroke(" ");
 7549    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7550    cx.simulate_keystroke("s");
 7551    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7552
 7553    cx.assert_editor_state(indoc! {"
 7554        one.second_completion
 7555        two sˇ
 7556        three sˇ
 7557        additional edit
 7558    "});
 7559    handle_completion_request(
 7560        &mut cx,
 7561        indoc! {"
 7562            one.second_completion
 7563            two s
 7564            three <s|>
 7565            additional edit
 7566        "},
 7567        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7568        counter.clone(),
 7569    )
 7570    .await;
 7571    cx.condition(|editor, _| editor.context_menu_visible())
 7572        .await;
 7573    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7574
 7575    cx.simulate_keystroke("i");
 7576
 7577    handle_completion_request(
 7578        &mut cx,
 7579        indoc! {"
 7580            one.second_completion
 7581            two si
 7582            three <si|>
 7583            additional edit
 7584        "},
 7585        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7586        counter.clone(),
 7587    )
 7588    .await;
 7589    cx.condition(|editor, _| editor.context_menu_visible())
 7590        .await;
 7591    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7592
 7593    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7594        editor
 7595            .confirm_completion(&ConfirmCompletion::default(), cx)
 7596            .unwrap()
 7597    });
 7598    cx.assert_editor_state(indoc! {"
 7599        one.second_completion
 7600        two sixth_completionˇ
 7601        three sixth_completionˇ
 7602        additional edit
 7603    "});
 7604
 7605    handle_resolve_completion_request(&mut cx, None).await;
 7606    apply_additional_edits.await.unwrap();
 7607
 7608    _ = cx.update(|cx| {
 7609        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7610            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7611                settings.show_completions_on_input = Some(false);
 7612            });
 7613        })
 7614    });
 7615    cx.set_state("editorˇ");
 7616    cx.simulate_keystroke(".");
 7617    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7618    cx.simulate_keystroke("c");
 7619    cx.simulate_keystroke("l");
 7620    cx.simulate_keystroke("o");
 7621    cx.assert_editor_state("editor.cloˇ");
 7622    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7623    cx.update_editor(|editor, cx| {
 7624        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7625    });
 7626    handle_completion_request(
 7627        &mut cx,
 7628        "editor.<clo|>",
 7629        vec!["close", "clobber"],
 7630        counter.clone(),
 7631    )
 7632    .await;
 7633    cx.condition(|editor, _| editor.context_menu_visible())
 7634        .await;
 7635    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7636
 7637    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7638        editor
 7639            .confirm_completion(&ConfirmCompletion::default(), cx)
 7640            .unwrap()
 7641    });
 7642    cx.assert_editor_state("editor.closeˇ");
 7643    handle_resolve_completion_request(&mut cx, None).await;
 7644    apply_additional_edits.await.unwrap();
 7645}
 7646
 7647#[gpui::test]
 7648async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7649    init_test(cx, |_| {});
 7650    let mut cx = EditorLspTestContext::new_rust(
 7651        lsp::ServerCapabilities {
 7652            completion_provider: Some(lsp::CompletionOptions {
 7653                trigger_characters: Some(vec![".".to_string()]),
 7654                ..Default::default()
 7655            }),
 7656            ..Default::default()
 7657        },
 7658        cx,
 7659    )
 7660    .await;
 7661    cx.lsp
 7662        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7663            Ok(Some(lsp::CompletionResponse::Array(vec![
 7664                lsp::CompletionItem {
 7665                    label: "first".into(),
 7666                    ..Default::default()
 7667                },
 7668                lsp::CompletionItem {
 7669                    label: "last".into(),
 7670                    ..Default::default()
 7671                },
 7672            ])))
 7673        });
 7674    cx.set_state("variableˇ");
 7675    cx.simulate_keystroke(".");
 7676    cx.executor().run_until_parked();
 7677
 7678    cx.update_editor(|editor, _| {
 7679        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7680            assert_eq!(
 7681                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7682                &["first", "last"]
 7683            );
 7684        } else {
 7685            panic!("expected completion menu to be open");
 7686        }
 7687    });
 7688
 7689    cx.update_editor(|editor, cx| {
 7690        editor.move_page_down(&MovePageDown::default(), cx);
 7691        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7692            assert!(
 7693                menu.selected_item == 1,
 7694                "expected PageDown to select the last item from the context menu"
 7695            );
 7696        } else {
 7697            panic!("expected completion menu to stay open after PageDown");
 7698        }
 7699    });
 7700
 7701    cx.update_editor(|editor, cx| {
 7702        editor.move_page_up(&MovePageUp::default(), cx);
 7703        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7704            assert!(
 7705                menu.selected_item == 0,
 7706                "expected PageUp to select the first item from the context menu"
 7707            );
 7708        } else {
 7709            panic!("expected completion menu to stay open after PageUp");
 7710        }
 7711    });
 7712}
 7713
 7714#[gpui::test]
 7715async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7716    init_test(cx, |_| {});
 7717
 7718    let mut cx = EditorLspTestContext::new_rust(
 7719        lsp::ServerCapabilities {
 7720            completion_provider: Some(lsp::CompletionOptions {
 7721                trigger_characters: Some(vec![".".to_string()]),
 7722                resolve_provider: Some(true),
 7723                ..Default::default()
 7724            }),
 7725            ..Default::default()
 7726        },
 7727        cx,
 7728    )
 7729    .await;
 7730
 7731    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7732    cx.simulate_keystroke(".");
 7733    let completion_item = lsp::CompletionItem {
 7734        label: "Some".into(),
 7735        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7736        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7737        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7738            kind: lsp::MarkupKind::Markdown,
 7739            value: "```rust\nSome(2)\n```".to_string(),
 7740        })),
 7741        deprecated: Some(false),
 7742        sort_text: Some("Some".to_string()),
 7743        filter_text: Some("Some".to_string()),
 7744        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7745        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7746            range: lsp::Range {
 7747                start: lsp::Position {
 7748                    line: 0,
 7749                    character: 22,
 7750                },
 7751                end: lsp::Position {
 7752                    line: 0,
 7753                    character: 22,
 7754                },
 7755            },
 7756            new_text: "Some(2)".to_string(),
 7757        })),
 7758        additional_text_edits: Some(vec![lsp::TextEdit {
 7759            range: lsp::Range {
 7760                start: lsp::Position {
 7761                    line: 0,
 7762                    character: 20,
 7763                },
 7764                end: lsp::Position {
 7765                    line: 0,
 7766                    character: 22,
 7767                },
 7768            },
 7769            new_text: "".to_string(),
 7770        }]),
 7771        ..Default::default()
 7772    };
 7773
 7774    let closure_completion_item = completion_item.clone();
 7775    let counter = Arc::new(AtomicUsize::new(0));
 7776    let counter_clone = counter.clone();
 7777    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7778        let task_completion_item = closure_completion_item.clone();
 7779        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7780        async move {
 7781            Ok(Some(lsp::CompletionResponse::Array(vec![
 7782                task_completion_item,
 7783            ])))
 7784        }
 7785    });
 7786
 7787    cx.condition(|editor, _| editor.context_menu_visible())
 7788        .await;
 7789    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7790    assert!(request.next().await.is_some());
 7791    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7792
 7793    cx.simulate_keystroke("S");
 7794    cx.simulate_keystroke("o");
 7795    cx.simulate_keystroke("m");
 7796    cx.condition(|editor, _| editor.context_menu_visible())
 7797        .await;
 7798    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7799    assert!(request.next().await.is_some());
 7800    assert!(request.next().await.is_some());
 7801    assert!(request.next().await.is_some());
 7802    request.close();
 7803    assert!(request.next().await.is_none());
 7804    assert_eq!(
 7805        counter.load(atomic::Ordering::Acquire),
 7806        4,
 7807        "With the completions menu open, only one LSP request should happen per input"
 7808    );
 7809}
 7810
 7811#[gpui::test]
 7812async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7813    init_test(cx, |_| {});
 7814    let mut cx = EditorTestContext::new(cx).await;
 7815    let language = Arc::new(Language::new(
 7816        LanguageConfig {
 7817            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7818            ..Default::default()
 7819        },
 7820        Some(tree_sitter_rust::language()),
 7821    ));
 7822    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7823
 7824    // If multiple selections intersect a line, the line is only toggled once.
 7825    cx.set_state(indoc! {"
 7826        fn a() {
 7827            «//b();
 7828            ˇ»// «c();
 7829            //ˇ»  d();
 7830        }
 7831    "});
 7832
 7833    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7834
 7835    cx.assert_editor_state(indoc! {"
 7836        fn a() {
 7837            «b();
 7838            c();
 7839            ˇ» d();
 7840        }
 7841    "});
 7842
 7843    // The comment prefix is inserted at the same column for every line in a
 7844    // selection.
 7845    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7846
 7847    cx.assert_editor_state(indoc! {"
 7848        fn a() {
 7849            // «b();
 7850            // c();
 7851            ˇ»//  d();
 7852        }
 7853    "});
 7854
 7855    // If a selection ends at the beginning of a line, that line is not toggled.
 7856    cx.set_selections_state(indoc! {"
 7857        fn a() {
 7858            // b();
 7859            «// c();
 7860        ˇ»    //  d();
 7861        }
 7862    "});
 7863
 7864    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7865
 7866    cx.assert_editor_state(indoc! {"
 7867        fn a() {
 7868            // b();
 7869            «c();
 7870        ˇ»    //  d();
 7871        }
 7872    "});
 7873
 7874    // If a selection span a single line and is empty, the line is toggled.
 7875    cx.set_state(indoc! {"
 7876        fn a() {
 7877            a();
 7878            b();
 7879        ˇ
 7880        }
 7881    "});
 7882
 7883    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7884
 7885    cx.assert_editor_state(indoc! {"
 7886        fn a() {
 7887            a();
 7888            b();
 7889        //•ˇ
 7890        }
 7891    "});
 7892
 7893    // If a selection span multiple lines, empty lines are not toggled.
 7894    cx.set_state(indoc! {"
 7895        fn a() {
 7896            «a();
 7897
 7898            c();ˇ»
 7899        }
 7900    "});
 7901
 7902    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7903
 7904    cx.assert_editor_state(indoc! {"
 7905        fn a() {
 7906            // «a();
 7907
 7908            // c();ˇ»
 7909        }
 7910    "});
 7911
 7912    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7913    cx.set_state(indoc! {"
 7914        fn a() {
 7915            «// a();
 7916            /// b();
 7917            //! c();ˇ»
 7918        }
 7919    "});
 7920
 7921    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7922
 7923    cx.assert_editor_state(indoc! {"
 7924        fn a() {
 7925            «a();
 7926            b();
 7927            c();ˇ»
 7928        }
 7929    "});
 7930}
 7931
 7932#[gpui::test]
 7933async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7934    init_test(cx, |_| {});
 7935
 7936    let language = Arc::new(Language::new(
 7937        LanguageConfig {
 7938            line_comments: vec!["// ".into()],
 7939            ..Default::default()
 7940        },
 7941        Some(tree_sitter_rust::language()),
 7942    ));
 7943
 7944    let mut cx = EditorTestContext::new(cx).await;
 7945
 7946    cx.language_registry().add(language.clone());
 7947    cx.update_buffer(|buffer, cx| {
 7948        buffer.set_language(Some(language), cx);
 7949    });
 7950
 7951    let toggle_comments = &ToggleComments {
 7952        advance_downwards: true,
 7953    };
 7954
 7955    // Single cursor on one line -> advance
 7956    // Cursor moves horizontally 3 characters as well on non-blank line
 7957    cx.set_state(indoc!(
 7958        "fn a() {
 7959             ˇdog();
 7960             cat();
 7961        }"
 7962    ));
 7963    cx.update_editor(|editor, cx| {
 7964        editor.toggle_comments(toggle_comments, cx);
 7965    });
 7966    cx.assert_editor_state(indoc!(
 7967        "fn a() {
 7968             // dog();
 7969             catˇ();
 7970        }"
 7971    ));
 7972
 7973    // Single selection on one line -> don't advance
 7974    cx.set_state(indoc!(
 7975        "fn a() {
 7976             «dog()ˇ»;
 7977             cat();
 7978        }"
 7979    ));
 7980    cx.update_editor(|editor, cx| {
 7981        editor.toggle_comments(toggle_comments, cx);
 7982    });
 7983    cx.assert_editor_state(indoc!(
 7984        "fn a() {
 7985             // «dog()ˇ»;
 7986             cat();
 7987        }"
 7988    ));
 7989
 7990    // Multiple cursors on one line -> advance
 7991    cx.set_state(indoc!(
 7992        "fn a() {
 7993             ˇdˇog();
 7994             cat();
 7995        }"
 7996    ));
 7997    cx.update_editor(|editor, cx| {
 7998        editor.toggle_comments(toggle_comments, cx);
 7999    });
 8000    cx.assert_editor_state(indoc!(
 8001        "fn a() {
 8002             // dog();
 8003             catˇ(ˇ);
 8004        }"
 8005    ));
 8006
 8007    // Multiple cursors on one line, with selection -> don't advance
 8008    cx.set_state(indoc!(
 8009        "fn a() {
 8010             ˇdˇog«()ˇ»;
 8011             cat();
 8012        }"
 8013    ));
 8014    cx.update_editor(|editor, cx| {
 8015        editor.toggle_comments(toggle_comments, cx);
 8016    });
 8017    cx.assert_editor_state(indoc!(
 8018        "fn a() {
 8019             // ˇdˇog«()ˇ»;
 8020             cat();
 8021        }"
 8022    ));
 8023
 8024    // Single cursor on one line -> advance
 8025    // Cursor moves to column 0 on blank line
 8026    cx.set_state(indoc!(
 8027        "fn a() {
 8028             ˇdog();
 8029
 8030             cat();
 8031        }"
 8032    ));
 8033    cx.update_editor(|editor, cx| {
 8034        editor.toggle_comments(toggle_comments, cx);
 8035    });
 8036    cx.assert_editor_state(indoc!(
 8037        "fn a() {
 8038             // dog();
 8039        ˇ
 8040             cat();
 8041        }"
 8042    ));
 8043
 8044    // Single cursor on one line -> advance
 8045    // Cursor starts and ends at column 0
 8046    cx.set_state(indoc!(
 8047        "fn a() {
 8048         ˇ    dog();
 8049             cat();
 8050        }"
 8051    ));
 8052    cx.update_editor(|editor, cx| {
 8053        editor.toggle_comments(toggle_comments, cx);
 8054    });
 8055    cx.assert_editor_state(indoc!(
 8056        "fn a() {
 8057             // dog();
 8058         ˇ    cat();
 8059        }"
 8060    ));
 8061}
 8062
 8063#[gpui::test]
 8064async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8065    init_test(cx, |_| {});
 8066
 8067    let mut cx = EditorTestContext::new(cx).await;
 8068
 8069    let html_language = Arc::new(
 8070        Language::new(
 8071            LanguageConfig {
 8072                name: "HTML".into(),
 8073                block_comment: Some(("<!-- ".into(), " -->".into())),
 8074                ..Default::default()
 8075            },
 8076            Some(tree_sitter_html::language()),
 8077        )
 8078        .with_injection_query(
 8079            r#"
 8080            (script_element
 8081                (raw_text) @content
 8082                (#set! "language" "javascript"))
 8083            "#,
 8084        )
 8085        .unwrap(),
 8086    );
 8087
 8088    let javascript_language = Arc::new(Language::new(
 8089        LanguageConfig {
 8090            name: "JavaScript".into(),
 8091            line_comments: vec!["// ".into()],
 8092            ..Default::default()
 8093        },
 8094        Some(tree_sitter_typescript::language_tsx()),
 8095    ));
 8096
 8097    cx.language_registry().add(html_language.clone());
 8098    cx.language_registry().add(javascript_language.clone());
 8099    cx.update_buffer(|buffer, cx| {
 8100        buffer.set_language(Some(html_language), cx);
 8101    });
 8102
 8103    // Toggle comments for empty selections
 8104    cx.set_state(
 8105        &r#"
 8106            <p>A</p>ˇ
 8107            <p>B</p>ˇ
 8108            <p>C</p>ˇ
 8109        "#
 8110        .unindent(),
 8111    );
 8112    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8113    cx.assert_editor_state(
 8114        &r#"
 8115            <!-- <p>A</p>ˇ -->
 8116            <!-- <p>B</p>ˇ -->
 8117            <!-- <p>C</p>ˇ -->
 8118        "#
 8119        .unindent(),
 8120    );
 8121    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8122    cx.assert_editor_state(
 8123        &r#"
 8124            <p>A</p>ˇ
 8125            <p>B</p>ˇ
 8126            <p>C</p>ˇ
 8127        "#
 8128        .unindent(),
 8129    );
 8130
 8131    // Toggle comments for mixture of empty and non-empty selections, where
 8132    // multiple selections occupy a given line.
 8133    cx.set_state(
 8134        &r#"
 8135            <p>A«</p>
 8136            <p>ˇ»B</p>ˇ
 8137            <p>C«</p>
 8138            <p>ˇ»D</p>ˇ
 8139        "#
 8140        .unindent(),
 8141    );
 8142
 8143    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8144    cx.assert_editor_state(
 8145        &r#"
 8146            <!-- <p>A«</p>
 8147            <p>ˇ»B</p>ˇ -->
 8148            <!-- <p>C«</p>
 8149            <p>ˇ»D</p>ˇ -->
 8150        "#
 8151        .unindent(),
 8152    );
 8153    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8154    cx.assert_editor_state(
 8155        &r#"
 8156            <p>A«</p>
 8157            <p>ˇ»B</p>ˇ
 8158            <p>C«</p>
 8159            <p>ˇ»D</p>ˇ
 8160        "#
 8161        .unindent(),
 8162    );
 8163
 8164    // Toggle comments when different languages are active for different
 8165    // selections.
 8166    cx.set_state(
 8167        &r#"
 8168            ˇ<script>
 8169                ˇvar x = new Y();
 8170            ˇ</script>
 8171        "#
 8172        .unindent(),
 8173    );
 8174    cx.executor().run_until_parked();
 8175    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8176    cx.assert_editor_state(
 8177        &r#"
 8178            <!-- ˇ<script> -->
 8179                // ˇvar x = new Y();
 8180            <!-- ˇ</script> -->
 8181        "#
 8182        .unindent(),
 8183    );
 8184}
 8185
 8186#[gpui::test]
 8187fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8188    init_test(cx, |_| {});
 8189
 8190    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8191    let multibuffer = cx.new_model(|cx| {
 8192        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8193        multibuffer.push_excerpts(
 8194            buffer.clone(),
 8195            [
 8196                ExcerptRange {
 8197                    context: Point::new(0, 0)..Point::new(0, 4),
 8198                    primary: None,
 8199                },
 8200                ExcerptRange {
 8201                    context: Point::new(1, 0)..Point::new(1, 4),
 8202                    primary: None,
 8203                },
 8204            ],
 8205            cx,
 8206        );
 8207        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8208        multibuffer
 8209    });
 8210
 8211    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8212    _ = view.update(cx, |view, cx| {
 8213        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8214        view.change_selections(None, cx, |s| {
 8215            s.select_ranges([
 8216                Point::new(0, 0)..Point::new(0, 0),
 8217                Point::new(1, 0)..Point::new(1, 0),
 8218            ])
 8219        });
 8220
 8221        view.handle_input("X", cx);
 8222        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8223        assert_eq!(
 8224            view.selections.ranges(cx),
 8225            [
 8226                Point::new(0, 1)..Point::new(0, 1),
 8227                Point::new(1, 1)..Point::new(1, 1),
 8228            ]
 8229        );
 8230
 8231        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8232        view.change_selections(None, cx, |s| {
 8233            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8234        });
 8235        view.backspace(&Default::default(), cx);
 8236        assert_eq!(view.text(cx), "Xa\nbbb");
 8237        assert_eq!(
 8238            view.selections.ranges(cx),
 8239            [Point::new(1, 0)..Point::new(1, 0)]
 8240        );
 8241
 8242        view.change_selections(None, cx, |s| {
 8243            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8244        });
 8245        view.backspace(&Default::default(), cx);
 8246        assert_eq!(view.text(cx), "X\nbb");
 8247        assert_eq!(
 8248            view.selections.ranges(cx),
 8249            [Point::new(0, 1)..Point::new(0, 1)]
 8250        );
 8251    });
 8252}
 8253
 8254#[gpui::test]
 8255fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8256    init_test(cx, |_| {});
 8257
 8258    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8259    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8260        indoc! {"
 8261            [aaaa
 8262            (bbbb]
 8263            cccc)",
 8264        },
 8265        markers.clone(),
 8266    );
 8267    let excerpt_ranges = markers.into_iter().map(|marker| {
 8268        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8269        ExcerptRange {
 8270            context,
 8271            primary: None,
 8272        }
 8273    });
 8274    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8275    let multibuffer = cx.new_model(|cx| {
 8276        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8277        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8278        multibuffer
 8279    });
 8280
 8281    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8282    _ = view.update(cx, |view, cx| {
 8283        let (expected_text, selection_ranges) = marked_text_ranges(
 8284            indoc! {"
 8285                aaaa
 8286                bˇbbb
 8287                bˇbbˇb
 8288                cccc"
 8289            },
 8290            true,
 8291        );
 8292        assert_eq!(view.text(cx), expected_text);
 8293        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8294
 8295        view.handle_input("X", cx);
 8296
 8297        let (expected_text, expected_selections) = marked_text_ranges(
 8298            indoc! {"
 8299                aaaa
 8300                bXˇbbXb
 8301                bXˇbbXˇb
 8302                cccc"
 8303            },
 8304            false,
 8305        );
 8306        assert_eq!(view.text(cx), expected_text);
 8307        assert_eq!(view.selections.ranges(cx), expected_selections);
 8308
 8309        view.newline(&Newline, cx);
 8310        let (expected_text, expected_selections) = marked_text_ranges(
 8311            indoc! {"
 8312                aaaa
 8313                bX
 8314                ˇbbX
 8315                b
 8316                bX
 8317                ˇbbX
 8318                ˇb
 8319                cccc"
 8320            },
 8321            false,
 8322        );
 8323        assert_eq!(view.text(cx), expected_text);
 8324        assert_eq!(view.selections.ranges(cx), expected_selections);
 8325    });
 8326}
 8327
 8328#[gpui::test]
 8329fn test_refresh_selections(cx: &mut TestAppContext) {
 8330    init_test(cx, |_| {});
 8331
 8332    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8333    let mut excerpt1_id = None;
 8334    let multibuffer = cx.new_model(|cx| {
 8335        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8336        excerpt1_id = multibuffer
 8337            .push_excerpts(
 8338                buffer.clone(),
 8339                [
 8340                    ExcerptRange {
 8341                        context: Point::new(0, 0)..Point::new(1, 4),
 8342                        primary: None,
 8343                    },
 8344                    ExcerptRange {
 8345                        context: Point::new(1, 0)..Point::new(2, 4),
 8346                        primary: None,
 8347                    },
 8348                ],
 8349                cx,
 8350            )
 8351            .into_iter()
 8352            .next();
 8353        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8354        multibuffer
 8355    });
 8356
 8357    let editor = cx.add_window(|cx| {
 8358        let mut editor = build_editor(multibuffer.clone(), cx);
 8359        let snapshot = editor.snapshot(cx);
 8360        editor.change_selections(None, cx, |s| {
 8361            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8362        });
 8363        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8364        assert_eq!(
 8365            editor.selections.ranges(cx),
 8366            [
 8367                Point::new(1, 3)..Point::new(1, 3),
 8368                Point::new(2, 1)..Point::new(2, 1),
 8369            ]
 8370        );
 8371        editor
 8372    });
 8373
 8374    // Refreshing selections is a no-op when excerpts haven't changed.
 8375    _ = editor.update(cx, |editor, cx| {
 8376        editor.change_selections(None, cx, |s| s.refresh());
 8377        assert_eq!(
 8378            editor.selections.ranges(cx),
 8379            [
 8380                Point::new(1, 3)..Point::new(1, 3),
 8381                Point::new(2, 1)..Point::new(2, 1),
 8382            ]
 8383        );
 8384    });
 8385
 8386    _ = multibuffer.update(cx, |multibuffer, cx| {
 8387        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8388    });
 8389    _ = editor.update(cx, |editor, cx| {
 8390        // Removing an excerpt causes the first selection to become degenerate.
 8391        assert_eq!(
 8392            editor.selections.ranges(cx),
 8393            [
 8394                Point::new(0, 0)..Point::new(0, 0),
 8395                Point::new(0, 1)..Point::new(0, 1)
 8396            ]
 8397        );
 8398
 8399        // Refreshing selections will relocate the first selection to the original buffer
 8400        // location.
 8401        editor.change_selections(None, cx, |s| s.refresh());
 8402        assert_eq!(
 8403            editor.selections.ranges(cx),
 8404            [
 8405                Point::new(0, 1)..Point::new(0, 1),
 8406                Point::new(0, 3)..Point::new(0, 3)
 8407            ]
 8408        );
 8409        assert!(editor.selections.pending_anchor().is_some());
 8410    });
 8411}
 8412
 8413#[gpui::test]
 8414fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8415    init_test(cx, |_| {});
 8416
 8417    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8418    let mut excerpt1_id = None;
 8419    let multibuffer = cx.new_model(|cx| {
 8420        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8421        excerpt1_id = multibuffer
 8422            .push_excerpts(
 8423                buffer.clone(),
 8424                [
 8425                    ExcerptRange {
 8426                        context: Point::new(0, 0)..Point::new(1, 4),
 8427                        primary: None,
 8428                    },
 8429                    ExcerptRange {
 8430                        context: Point::new(1, 0)..Point::new(2, 4),
 8431                        primary: None,
 8432                    },
 8433                ],
 8434                cx,
 8435            )
 8436            .into_iter()
 8437            .next();
 8438        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8439        multibuffer
 8440    });
 8441
 8442    let editor = cx.add_window(|cx| {
 8443        let mut editor = build_editor(multibuffer.clone(), cx);
 8444        let snapshot = editor.snapshot(cx);
 8445        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8446        assert_eq!(
 8447            editor.selections.ranges(cx),
 8448            [Point::new(1, 3)..Point::new(1, 3)]
 8449        );
 8450        editor
 8451    });
 8452
 8453    _ = multibuffer.update(cx, |multibuffer, cx| {
 8454        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8455    });
 8456    _ = editor.update(cx, |editor, cx| {
 8457        assert_eq!(
 8458            editor.selections.ranges(cx),
 8459            [Point::new(0, 0)..Point::new(0, 0)]
 8460        );
 8461
 8462        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8463        editor.change_selections(None, cx, |s| s.refresh());
 8464        assert_eq!(
 8465            editor.selections.ranges(cx),
 8466            [Point::new(0, 3)..Point::new(0, 3)]
 8467        );
 8468        assert!(editor.selections.pending_anchor().is_some());
 8469    });
 8470}
 8471
 8472#[gpui::test]
 8473async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8474    init_test(cx, |_| {});
 8475
 8476    let language = Arc::new(
 8477        Language::new(
 8478            LanguageConfig {
 8479                brackets: BracketPairConfig {
 8480                    pairs: vec![
 8481                        BracketPair {
 8482                            start: "{".to_string(),
 8483                            end: "}".to_string(),
 8484                            close: true,
 8485                            surround: true,
 8486                            newline: true,
 8487                        },
 8488                        BracketPair {
 8489                            start: "/* ".to_string(),
 8490                            end: " */".to_string(),
 8491                            close: true,
 8492                            surround: true,
 8493                            newline: true,
 8494                        },
 8495                    ],
 8496                    ..Default::default()
 8497                },
 8498                ..Default::default()
 8499            },
 8500            Some(tree_sitter_rust::language()),
 8501        )
 8502        .with_indents_query("")
 8503        .unwrap(),
 8504    );
 8505
 8506    let text = concat!(
 8507        "{   }\n",     //
 8508        "  x\n",       //
 8509        "  /*   */\n", //
 8510        "x\n",         //
 8511        "{{} }\n",     //
 8512    );
 8513
 8514    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8515    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8516    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8517    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8518        .await;
 8519
 8520    _ = view.update(cx, |view, cx| {
 8521        view.change_selections(None, cx, |s| {
 8522            s.select_display_ranges([
 8523                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8524                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8525                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8526            ])
 8527        });
 8528        view.newline(&Newline, cx);
 8529
 8530        assert_eq!(
 8531            view.buffer().read(cx).read(cx).text(),
 8532            concat!(
 8533                "{ \n",    // Suppress rustfmt
 8534                "\n",      //
 8535                "}\n",     //
 8536                "  x\n",   //
 8537                "  /* \n", //
 8538                "  \n",    //
 8539                "  */\n",  //
 8540                "x\n",     //
 8541                "{{} \n",  //
 8542                "}\n",     //
 8543            )
 8544        );
 8545    });
 8546}
 8547
 8548#[gpui::test]
 8549fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8550    init_test(cx, |_| {});
 8551
 8552    let editor = cx.add_window(|cx| {
 8553        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8554        build_editor(buffer.clone(), cx)
 8555    });
 8556
 8557    _ = editor.update(cx, |editor, cx| {
 8558        struct Type1;
 8559        struct Type2;
 8560
 8561        let buffer = editor.buffer.read(cx).snapshot(cx);
 8562
 8563        let anchor_range =
 8564            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8565
 8566        editor.highlight_background::<Type1>(
 8567            &[
 8568                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8569                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8570                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8571                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8572            ],
 8573            |_| Hsla::red(),
 8574            cx,
 8575        );
 8576        editor.highlight_background::<Type2>(
 8577            &[
 8578                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8579                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8580                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8581                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8582            ],
 8583            |_| Hsla::green(),
 8584            cx,
 8585        );
 8586
 8587        let snapshot = editor.snapshot(cx);
 8588        let mut highlighted_ranges = editor.background_highlights_in_range(
 8589            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8590            &snapshot,
 8591            cx.theme().colors(),
 8592        );
 8593        // Enforce a consistent ordering based on color without relying on the ordering of the
 8594        // highlight's `TypeId` which is non-executor.
 8595        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8596        assert_eq!(
 8597            highlighted_ranges,
 8598            &[
 8599                (
 8600                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8601                    Hsla::red(),
 8602                ),
 8603                (
 8604                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8605                    Hsla::red(),
 8606                ),
 8607                (
 8608                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8609                    Hsla::green(),
 8610                ),
 8611                (
 8612                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8613                    Hsla::green(),
 8614                ),
 8615            ]
 8616        );
 8617        assert_eq!(
 8618            editor.background_highlights_in_range(
 8619                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8620                &snapshot,
 8621                cx.theme().colors(),
 8622            ),
 8623            &[(
 8624                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8625                Hsla::red(),
 8626            )]
 8627        );
 8628    });
 8629}
 8630
 8631#[gpui::test]
 8632async fn test_following(cx: &mut gpui::TestAppContext) {
 8633    init_test(cx, |_| {});
 8634
 8635    let fs = FakeFs::new(cx.executor());
 8636    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8637
 8638    let buffer = project.update(cx, |project, cx| {
 8639        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8640        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8641    });
 8642    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8643    let follower = cx.update(|cx| {
 8644        cx.open_window(
 8645            WindowOptions {
 8646                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8647                    gpui::Point::new(px(0.), px(0.)),
 8648                    gpui::Point::new(px(10.), px(80.)),
 8649                ))),
 8650                ..Default::default()
 8651            },
 8652            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8653        )
 8654        .unwrap()
 8655    });
 8656
 8657    let is_still_following = Rc::new(RefCell::new(true));
 8658    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8659    let pending_update = Rc::new(RefCell::new(None));
 8660    _ = follower.update(cx, {
 8661        let update = pending_update.clone();
 8662        let is_still_following = is_still_following.clone();
 8663        let follower_edit_event_count = follower_edit_event_count.clone();
 8664        |_, cx| {
 8665            cx.subscribe(
 8666                &leader.root_view(cx).unwrap(),
 8667                move |_, leader, event, cx| {
 8668                    leader
 8669                        .read(cx)
 8670                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8671                },
 8672            )
 8673            .detach();
 8674
 8675            cx.subscribe(
 8676                &follower.root_view(cx).unwrap(),
 8677                move |_, _, event: &EditorEvent, _cx| {
 8678                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8679                        *is_still_following.borrow_mut() = false;
 8680                    }
 8681
 8682                    if let EditorEvent::BufferEdited = event {
 8683                        *follower_edit_event_count.borrow_mut() += 1;
 8684                    }
 8685                },
 8686            )
 8687            .detach();
 8688        }
 8689    });
 8690
 8691    // Update the selections only
 8692    _ = leader.update(cx, |leader, cx| {
 8693        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8694    });
 8695    follower
 8696        .update(cx, |follower, cx| {
 8697            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8698        })
 8699        .unwrap()
 8700        .await
 8701        .unwrap();
 8702    _ = follower.update(cx, |follower, cx| {
 8703        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8704    });
 8705    assert_eq!(*is_still_following.borrow(), true);
 8706    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8707
 8708    // Update the scroll position only
 8709    _ = leader.update(cx, |leader, cx| {
 8710        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8711    });
 8712    follower
 8713        .update(cx, |follower, cx| {
 8714            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8715        })
 8716        .unwrap()
 8717        .await
 8718        .unwrap();
 8719    assert_eq!(
 8720        follower
 8721            .update(cx, |follower, cx| follower.scroll_position(cx))
 8722            .unwrap(),
 8723        gpui::Point::new(1.5, 3.5)
 8724    );
 8725    assert_eq!(*is_still_following.borrow(), true);
 8726    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8727
 8728    // Update the selections and scroll position. The follower's scroll position is updated
 8729    // via autoscroll, not via the leader's exact scroll position.
 8730    _ = leader.update(cx, |leader, cx| {
 8731        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8732        leader.request_autoscroll(Autoscroll::newest(), cx);
 8733        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8734    });
 8735    follower
 8736        .update(cx, |follower, cx| {
 8737            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8738        })
 8739        .unwrap()
 8740        .await
 8741        .unwrap();
 8742    _ = follower.update(cx, |follower, cx| {
 8743        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8744        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8745    });
 8746    assert_eq!(*is_still_following.borrow(), true);
 8747
 8748    // Creating a pending selection that precedes another selection
 8749    _ = leader.update(cx, |leader, cx| {
 8750        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8751        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8752    });
 8753    follower
 8754        .update(cx, |follower, cx| {
 8755            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8756        })
 8757        .unwrap()
 8758        .await
 8759        .unwrap();
 8760    _ = follower.update(cx, |follower, cx| {
 8761        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8762    });
 8763    assert_eq!(*is_still_following.borrow(), true);
 8764
 8765    // Extend the pending selection so that it surrounds another selection
 8766    _ = leader.update(cx, |leader, cx| {
 8767        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8768    });
 8769    follower
 8770        .update(cx, |follower, cx| {
 8771            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8772        })
 8773        .unwrap()
 8774        .await
 8775        .unwrap();
 8776    _ = follower.update(cx, |follower, cx| {
 8777        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8778    });
 8779
 8780    // Scrolling locally breaks the follow
 8781    _ = follower.update(cx, |follower, cx| {
 8782        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8783        follower.set_scroll_anchor(
 8784            ScrollAnchor {
 8785                anchor: top_anchor,
 8786                offset: gpui::Point::new(0.0, 0.5),
 8787            },
 8788            cx,
 8789        );
 8790    });
 8791    assert_eq!(*is_still_following.borrow(), false);
 8792}
 8793
 8794#[gpui::test]
 8795async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8796    init_test(cx, |_| {});
 8797
 8798    let fs = FakeFs::new(cx.executor());
 8799    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8800    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8801    let pane = workspace
 8802        .update(cx, |workspace, _| workspace.active_pane().clone())
 8803        .unwrap();
 8804
 8805    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8806
 8807    let leader = pane.update(cx, |_, cx| {
 8808        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8809        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8810    });
 8811
 8812    // Start following the editor when it has no excerpts.
 8813    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8814    let follower_1 = cx
 8815        .update_window(*workspace.deref(), |_, cx| {
 8816            Editor::from_state_proto(
 8817                workspace.root_view(cx).unwrap(),
 8818                ViewId {
 8819                    creator: Default::default(),
 8820                    id: 0,
 8821                },
 8822                &mut state_message,
 8823                cx,
 8824            )
 8825        })
 8826        .unwrap()
 8827        .unwrap()
 8828        .await
 8829        .unwrap();
 8830
 8831    let update_message = Rc::new(RefCell::new(None));
 8832    follower_1.update(cx, {
 8833        let update = update_message.clone();
 8834        |_, cx| {
 8835            cx.subscribe(&leader, move |_, leader, event, cx| {
 8836                leader
 8837                    .read(cx)
 8838                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8839            })
 8840            .detach();
 8841        }
 8842    });
 8843
 8844    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8845        (
 8846            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8847            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8848        )
 8849    });
 8850
 8851    // Insert some excerpts.
 8852    _ = leader.update(cx, |leader, cx| {
 8853        leader.buffer.update(cx, |multibuffer, cx| {
 8854            let excerpt_ids = multibuffer.push_excerpts(
 8855                buffer_1.clone(),
 8856                [
 8857                    ExcerptRange {
 8858                        context: 1..6,
 8859                        primary: None,
 8860                    },
 8861                    ExcerptRange {
 8862                        context: 12..15,
 8863                        primary: None,
 8864                    },
 8865                    ExcerptRange {
 8866                        context: 0..3,
 8867                        primary: None,
 8868                    },
 8869                ],
 8870                cx,
 8871            );
 8872            multibuffer.insert_excerpts_after(
 8873                excerpt_ids[0],
 8874                buffer_2.clone(),
 8875                [
 8876                    ExcerptRange {
 8877                        context: 8..12,
 8878                        primary: None,
 8879                    },
 8880                    ExcerptRange {
 8881                        context: 0..6,
 8882                        primary: None,
 8883                    },
 8884                ],
 8885                cx,
 8886            );
 8887        });
 8888    });
 8889
 8890    // Apply the update of adding the excerpts.
 8891    follower_1
 8892        .update(cx, |follower, cx| {
 8893            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8894        })
 8895        .await
 8896        .unwrap();
 8897    assert_eq!(
 8898        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8899        leader.update(cx, |editor, cx| editor.text(cx))
 8900    );
 8901    update_message.borrow_mut().take();
 8902
 8903    // Start following separately after it already has excerpts.
 8904    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8905    let follower_2 = cx
 8906        .update_window(*workspace.deref(), |_, cx| {
 8907            Editor::from_state_proto(
 8908                workspace.root_view(cx).unwrap().clone(),
 8909                ViewId {
 8910                    creator: Default::default(),
 8911                    id: 0,
 8912                },
 8913                &mut state_message,
 8914                cx,
 8915            )
 8916        })
 8917        .unwrap()
 8918        .unwrap()
 8919        .await
 8920        .unwrap();
 8921    assert_eq!(
 8922        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8923        leader.update(cx, |editor, cx| editor.text(cx))
 8924    );
 8925
 8926    // Remove some excerpts.
 8927    _ = leader.update(cx, |leader, cx| {
 8928        leader.buffer.update(cx, |multibuffer, cx| {
 8929            let excerpt_ids = multibuffer.excerpt_ids();
 8930            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8931            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8932        });
 8933    });
 8934
 8935    // Apply the update of removing the excerpts.
 8936    follower_1
 8937        .update(cx, |follower, cx| {
 8938            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8939        })
 8940        .await
 8941        .unwrap();
 8942    follower_2
 8943        .update(cx, |follower, cx| {
 8944            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8945        })
 8946        .await
 8947        .unwrap();
 8948    update_message.borrow_mut().take();
 8949    assert_eq!(
 8950        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8951        leader.update(cx, |editor, cx| editor.text(cx))
 8952    );
 8953}
 8954
 8955#[gpui::test]
 8956async fn go_to_prev_overlapping_diagnostic(
 8957    executor: BackgroundExecutor,
 8958    cx: &mut gpui::TestAppContext,
 8959) {
 8960    init_test(cx, |_| {});
 8961
 8962    let mut cx = EditorTestContext::new(cx).await;
 8963    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 8964
 8965    cx.set_state(indoc! {"
 8966        ˇfn func(abc def: i32) -> u32 {
 8967        }
 8968    "});
 8969
 8970    _ = cx.update(|cx| {
 8971        _ = project.update(cx, |project, cx| {
 8972            project
 8973                .update_diagnostics(
 8974                    LanguageServerId(0),
 8975                    lsp::PublishDiagnosticsParams {
 8976                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 8977                        version: None,
 8978                        diagnostics: vec![
 8979                            lsp::Diagnostic {
 8980                                range: lsp::Range::new(
 8981                                    lsp::Position::new(0, 11),
 8982                                    lsp::Position::new(0, 12),
 8983                                ),
 8984                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8985                                ..Default::default()
 8986                            },
 8987                            lsp::Diagnostic {
 8988                                range: lsp::Range::new(
 8989                                    lsp::Position::new(0, 12),
 8990                                    lsp::Position::new(0, 15),
 8991                                ),
 8992                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8993                                ..Default::default()
 8994                            },
 8995                            lsp::Diagnostic {
 8996                                range: lsp::Range::new(
 8997                                    lsp::Position::new(0, 25),
 8998                                    lsp::Position::new(0, 28),
 8999                                ),
 9000                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9001                                ..Default::default()
 9002                            },
 9003                        ],
 9004                    },
 9005                    &[],
 9006                    cx,
 9007                )
 9008                .unwrap()
 9009        });
 9010    });
 9011
 9012    executor.run_until_parked();
 9013
 9014    cx.update_editor(|editor, cx| {
 9015        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9016    });
 9017
 9018    cx.assert_editor_state(indoc! {"
 9019        fn func(abc def: i32) -> ˇu32 {
 9020        }
 9021    "});
 9022
 9023    cx.update_editor(|editor, cx| {
 9024        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9025    });
 9026
 9027    cx.assert_editor_state(indoc! {"
 9028        fn func(abc ˇdef: i32) -> u32 {
 9029        }
 9030    "});
 9031
 9032    cx.update_editor(|editor, cx| {
 9033        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9034    });
 9035
 9036    cx.assert_editor_state(indoc! {"
 9037        fn func(abcˇ def: i32) -> u32 {
 9038        }
 9039    "});
 9040
 9041    cx.update_editor(|editor, cx| {
 9042        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9043    });
 9044
 9045    cx.assert_editor_state(indoc! {"
 9046        fn func(abc def: i32) -> ˇu32 {
 9047        }
 9048    "});
 9049}
 9050
 9051#[gpui::test]
 9052async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9053    init_test(cx, |_| {});
 9054
 9055    let mut cx = EditorTestContext::new(cx).await;
 9056
 9057    let diff_base = r#"
 9058        use some::mod;
 9059
 9060        const A: u32 = 42;
 9061
 9062        fn main() {
 9063            println!("hello");
 9064
 9065            println!("world");
 9066        }
 9067        "#
 9068    .unindent();
 9069
 9070    // Edits are modified, removed, modified, added
 9071    cx.set_state(
 9072        &r#"
 9073        use some::modified;
 9074
 9075        ˇ
 9076        fn main() {
 9077            println!("hello there");
 9078
 9079            println!("around the");
 9080            println!("world");
 9081        }
 9082        "#
 9083        .unindent(),
 9084    );
 9085
 9086    cx.set_diff_base(Some(&diff_base));
 9087    executor.run_until_parked();
 9088
 9089    cx.update_editor(|editor, cx| {
 9090        //Wrap around the bottom of the buffer
 9091        for _ in 0..3 {
 9092            editor.go_to_hunk(&GoToHunk, cx);
 9093        }
 9094    });
 9095
 9096    cx.assert_editor_state(
 9097        &r#"
 9098        ˇuse some::modified;
 9099
 9100
 9101        fn main() {
 9102            println!("hello there");
 9103
 9104            println!("around the");
 9105            println!("world");
 9106        }
 9107        "#
 9108        .unindent(),
 9109    );
 9110
 9111    cx.update_editor(|editor, cx| {
 9112        //Wrap around the top of the buffer
 9113        for _ in 0..2 {
 9114            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9115        }
 9116    });
 9117
 9118    cx.assert_editor_state(
 9119        &r#"
 9120        use some::modified;
 9121
 9122
 9123        fn main() {
 9124        ˇ    println!("hello there");
 9125
 9126            println!("around the");
 9127            println!("world");
 9128        }
 9129        "#
 9130        .unindent(),
 9131    );
 9132
 9133    cx.update_editor(|editor, cx| {
 9134        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9135    });
 9136
 9137    cx.assert_editor_state(
 9138        &r#"
 9139        use some::modified;
 9140
 9141        ˇ
 9142        fn main() {
 9143            println!("hello there");
 9144
 9145            println!("around the");
 9146            println!("world");
 9147        }
 9148        "#
 9149        .unindent(),
 9150    );
 9151
 9152    cx.update_editor(|editor, cx| {
 9153        for _ in 0..3 {
 9154            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9155        }
 9156    });
 9157
 9158    cx.assert_editor_state(
 9159        &r#"
 9160        use some::modified;
 9161
 9162
 9163        fn main() {
 9164        ˇ    println!("hello there");
 9165
 9166            println!("around the");
 9167            println!("world");
 9168        }
 9169        "#
 9170        .unindent(),
 9171    );
 9172
 9173    cx.update_editor(|editor, cx| {
 9174        editor.fold(&Fold, cx);
 9175
 9176        //Make sure that the fold only gets one hunk
 9177        for _ in 0..4 {
 9178            editor.go_to_hunk(&GoToHunk, cx);
 9179        }
 9180    });
 9181
 9182    cx.assert_editor_state(
 9183        &r#"
 9184        ˇuse some::modified;
 9185
 9186
 9187        fn main() {
 9188            println!("hello there");
 9189
 9190            println!("around the");
 9191            println!("world");
 9192        }
 9193        "#
 9194        .unindent(),
 9195    );
 9196}
 9197
 9198#[test]
 9199fn test_split_words() {
 9200    fn split(text: &str) -> Vec<&str> {
 9201        split_words(text).collect()
 9202    }
 9203
 9204    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9205    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9206    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9207    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9208    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9209    assert_eq!(split("helloworld"), &["helloworld"]);
 9210
 9211    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9212}
 9213
 9214#[gpui::test]
 9215async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9216    init_test(cx, |_| {});
 9217
 9218    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9219    let mut assert = |before, after| {
 9220        let _state_context = cx.set_state(before);
 9221        cx.update_editor(|editor, cx| {
 9222            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9223        });
 9224        cx.assert_editor_state(after);
 9225    };
 9226
 9227    // Outside bracket jumps to outside of matching bracket
 9228    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9229    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9230
 9231    // Inside bracket jumps to inside of matching bracket
 9232    assert("console.log(ˇvar);", "console.log(varˇ);");
 9233    assert("console.log(varˇ);", "console.log(ˇvar);");
 9234
 9235    // When outside a bracket and inside, favor jumping to the inside bracket
 9236    assert(
 9237        "console.log('foo', [1, 2, 3]ˇ);",
 9238        "console.log(ˇ'foo', [1, 2, 3]);",
 9239    );
 9240    assert(
 9241        "console.log(ˇ'foo', [1, 2, 3]);",
 9242        "console.log('foo', [1, 2, 3]ˇ);",
 9243    );
 9244
 9245    // Bias forward if two options are equally likely
 9246    assert(
 9247        "let result = curried_fun()ˇ();",
 9248        "let result = curried_fun()()ˇ;",
 9249    );
 9250
 9251    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9252    assert(
 9253        indoc! {"
 9254            function test() {
 9255                console.log('test')ˇ
 9256            }"},
 9257        indoc! {"
 9258            function test() {
 9259                console.logˇ('test')
 9260            }"},
 9261    );
 9262}
 9263
 9264#[gpui::test]
 9265async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9266    init_test(cx, |_| {});
 9267
 9268    let fs = FakeFs::new(cx.executor());
 9269    fs.insert_tree(
 9270        "/a",
 9271        json!({
 9272            "main.rs": "fn main() { let a = 5; }",
 9273            "other.rs": "// Test file",
 9274        }),
 9275    )
 9276    .await;
 9277    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9278
 9279    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9280    language_registry.add(Arc::new(Language::new(
 9281        LanguageConfig {
 9282            name: "Rust".into(),
 9283            matcher: LanguageMatcher {
 9284                path_suffixes: vec!["rs".to_string()],
 9285                ..Default::default()
 9286            },
 9287            brackets: BracketPairConfig {
 9288                pairs: vec![BracketPair {
 9289                    start: "{".to_string(),
 9290                    end: "}".to_string(),
 9291                    close: true,
 9292                    surround: true,
 9293                    newline: true,
 9294                }],
 9295                disabled_scopes_by_bracket_ix: Vec::new(),
 9296            },
 9297            ..Default::default()
 9298        },
 9299        Some(tree_sitter_rust::language()),
 9300    )));
 9301    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9302        "Rust",
 9303        FakeLspAdapter {
 9304            capabilities: lsp::ServerCapabilities {
 9305                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9306                    first_trigger_character: "{".to_string(),
 9307                    more_trigger_character: None,
 9308                }),
 9309                ..Default::default()
 9310            },
 9311            ..Default::default()
 9312        },
 9313    );
 9314
 9315    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9316
 9317    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9318
 9319    let worktree_id = workspace
 9320        .update(cx, |workspace, cx| {
 9321            workspace.project().update(cx, |project, cx| {
 9322                project.worktrees(cx).next().unwrap().read(cx).id()
 9323            })
 9324        })
 9325        .unwrap();
 9326
 9327    let buffer = project
 9328        .update(cx, |project, cx| {
 9329            project.open_local_buffer("/a/main.rs", cx)
 9330        })
 9331        .await
 9332        .unwrap();
 9333    cx.executor().run_until_parked();
 9334    cx.executor().start_waiting();
 9335    let fake_server = fake_servers.next().await.unwrap();
 9336    let editor_handle = workspace
 9337        .update(cx, |workspace, cx| {
 9338            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9339        })
 9340        .unwrap()
 9341        .await
 9342        .unwrap()
 9343        .downcast::<Editor>()
 9344        .unwrap();
 9345
 9346    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9347        assert_eq!(
 9348            params.text_document_position.text_document.uri,
 9349            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9350        );
 9351        assert_eq!(
 9352            params.text_document_position.position,
 9353            lsp::Position::new(0, 21),
 9354        );
 9355
 9356        Ok(Some(vec![lsp::TextEdit {
 9357            new_text: "]".to_string(),
 9358            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9359        }]))
 9360    });
 9361
 9362    editor_handle.update(cx, |editor, cx| {
 9363        editor.focus(cx);
 9364        editor.change_selections(None, cx, |s| {
 9365            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9366        });
 9367        editor.handle_input("{", cx);
 9368    });
 9369
 9370    cx.executor().run_until_parked();
 9371
 9372    _ = buffer.update(cx, |buffer, _| {
 9373        assert_eq!(
 9374            buffer.text(),
 9375            "fn main() { let a = {5}; }",
 9376            "No extra braces from on type formatting should appear in the buffer"
 9377        )
 9378    });
 9379}
 9380
 9381#[gpui::test]
 9382async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9383    init_test(cx, |_| {});
 9384
 9385    let fs = FakeFs::new(cx.executor());
 9386    fs.insert_tree(
 9387        "/a",
 9388        json!({
 9389            "main.rs": "fn main() { let a = 5; }",
 9390            "other.rs": "// Test file",
 9391        }),
 9392    )
 9393    .await;
 9394
 9395    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9396
 9397    let server_restarts = Arc::new(AtomicUsize::new(0));
 9398    let closure_restarts = Arc::clone(&server_restarts);
 9399    let language_server_name = "test language server";
 9400    let language_name: Arc<str> = "Rust".into();
 9401
 9402    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9403    language_registry.add(Arc::new(Language::new(
 9404        LanguageConfig {
 9405            name: Arc::clone(&language_name),
 9406            matcher: LanguageMatcher {
 9407                path_suffixes: vec!["rs".to_string()],
 9408                ..Default::default()
 9409            },
 9410            ..Default::default()
 9411        },
 9412        Some(tree_sitter_rust::language()),
 9413    )));
 9414    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9415        "Rust",
 9416        FakeLspAdapter {
 9417            name: language_server_name,
 9418            initialization_options: Some(json!({
 9419                "testOptionValue": true
 9420            })),
 9421            initializer: Some(Box::new(move |fake_server| {
 9422                let task_restarts = Arc::clone(&closure_restarts);
 9423                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9424                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9425                    futures::future::ready(Ok(()))
 9426                });
 9427            })),
 9428            ..Default::default()
 9429        },
 9430    );
 9431
 9432    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9433    let _buffer = project
 9434        .update(cx, |project, cx| {
 9435            project.open_local_buffer("/a/main.rs", cx)
 9436        })
 9437        .await
 9438        .unwrap();
 9439    let _fake_server = fake_servers.next().await.unwrap();
 9440    update_test_language_settings(cx, |language_settings| {
 9441        language_settings.languages.insert(
 9442            Arc::clone(&language_name),
 9443            LanguageSettingsContent {
 9444                tab_size: NonZeroU32::new(8),
 9445                ..Default::default()
 9446            },
 9447        );
 9448    });
 9449    cx.executor().run_until_parked();
 9450    assert_eq!(
 9451        server_restarts.load(atomic::Ordering::Acquire),
 9452        0,
 9453        "Should not restart LSP server on an unrelated change"
 9454    );
 9455
 9456    update_test_project_settings(cx, |project_settings| {
 9457        project_settings.lsp.insert(
 9458            "Some other server name".into(),
 9459            LspSettings {
 9460                binary: None,
 9461                settings: None,
 9462                initialization_options: Some(json!({
 9463                    "some other init value": false
 9464                })),
 9465            },
 9466        );
 9467    });
 9468    cx.executor().run_until_parked();
 9469    assert_eq!(
 9470        server_restarts.load(atomic::Ordering::Acquire),
 9471        0,
 9472        "Should not restart LSP server on an unrelated LSP settings change"
 9473    );
 9474
 9475    update_test_project_settings(cx, |project_settings| {
 9476        project_settings.lsp.insert(
 9477            language_server_name.into(),
 9478            LspSettings {
 9479                binary: None,
 9480                settings: None,
 9481                initialization_options: Some(json!({
 9482                    "anotherInitValue": false
 9483                })),
 9484            },
 9485        );
 9486    });
 9487    cx.executor().run_until_parked();
 9488    assert_eq!(
 9489        server_restarts.load(atomic::Ordering::Acquire),
 9490        1,
 9491        "Should restart LSP server on a related LSP settings change"
 9492    );
 9493
 9494    update_test_project_settings(cx, |project_settings| {
 9495        project_settings.lsp.insert(
 9496            language_server_name.into(),
 9497            LspSettings {
 9498                binary: None,
 9499                settings: None,
 9500                initialization_options: Some(json!({
 9501                    "anotherInitValue": false
 9502                })),
 9503            },
 9504        );
 9505    });
 9506    cx.executor().run_until_parked();
 9507    assert_eq!(
 9508        server_restarts.load(atomic::Ordering::Acquire),
 9509        1,
 9510        "Should not restart LSP server on a related LSP settings change that is the same"
 9511    );
 9512
 9513    update_test_project_settings(cx, |project_settings| {
 9514        project_settings.lsp.insert(
 9515            language_server_name.into(),
 9516            LspSettings {
 9517                binary: None,
 9518                settings: None,
 9519                initialization_options: None,
 9520            },
 9521        );
 9522    });
 9523    cx.executor().run_until_parked();
 9524    assert_eq!(
 9525        server_restarts.load(atomic::Ordering::Acquire),
 9526        2,
 9527        "Should restart LSP server on another related LSP settings change"
 9528    );
 9529}
 9530
 9531#[gpui::test]
 9532async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9533    init_test(cx, |_| {});
 9534
 9535    let mut cx = EditorLspTestContext::new_rust(
 9536        lsp::ServerCapabilities {
 9537            completion_provider: Some(lsp::CompletionOptions {
 9538                trigger_characters: Some(vec![".".to_string()]),
 9539                resolve_provider: Some(true),
 9540                ..Default::default()
 9541            }),
 9542            ..Default::default()
 9543        },
 9544        cx,
 9545    )
 9546    .await;
 9547
 9548    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9549    cx.simulate_keystroke(".");
 9550    let completion_item = lsp::CompletionItem {
 9551        label: "some".into(),
 9552        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9553        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9554        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9555            kind: lsp::MarkupKind::Markdown,
 9556            value: "```rust\nSome(2)\n```".to_string(),
 9557        })),
 9558        deprecated: Some(false),
 9559        sort_text: Some("fffffff2".to_string()),
 9560        filter_text: Some("some".to_string()),
 9561        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9562        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9563            range: lsp::Range {
 9564                start: lsp::Position {
 9565                    line: 0,
 9566                    character: 22,
 9567                },
 9568                end: lsp::Position {
 9569                    line: 0,
 9570                    character: 22,
 9571                },
 9572            },
 9573            new_text: "Some(2)".to_string(),
 9574        })),
 9575        additional_text_edits: Some(vec![lsp::TextEdit {
 9576            range: lsp::Range {
 9577                start: lsp::Position {
 9578                    line: 0,
 9579                    character: 20,
 9580                },
 9581                end: lsp::Position {
 9582                    line: 0,
 9583                    character: 22,
 9584                },
 9585            },
 9586            new_text: "".to_string(),
 9587        }]),
 9588        ..Default::default()
 9589    };
 9590
 9591    let closure_completion_item = completion_item.clone();
 9592    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9593        let task_completion_item = closure_completion_item.clone();
 9594        async move {
 9595            Ok(Some(lsp::CompletionResponse::Array(vec![
 9596                task_completion_item,
 9597            ])))
 9598        }
 9599    });
 9600
 9601    request.next().await;
 9602
 9603    cx.condition(|editor, _| editor.context_menu_visible())
 9604        .await;
 9605    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9606        editor
 9607            .confirm_completion(&ConfirmCompletion::default(), cx)
 9608            .unwrap()
 9609    });
 9610    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9611
 9612    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9613        let task_completion_item = completion_item.clone();
 9614        async move { Ok(task_completion_item) }
 9615    })
 9616    .next()
 9617    .await
 9618    .unwrap();
 9619    apply_additional_edits.await.unwrap();
 9620    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9621}
 9622
 9623#[gpui::test]
 9624async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9625    init_test(cx, |_| {});
 9626
 9627    let mut cx = EditorLspTestContext::new(
 9628        Language::new(
 9629            LanguageConfig {
 9630                matcher: LanguageMatcher {
 9631                    path_suffixes: vec!["jsx".into()],
 9632                    ..Default::default()
 9633                },
 9634                overrides: [(
 9635                    "element".into(),
 9636                    LanguageConfigOverride {
 9637                        word_characters: Override::Set(['-'].into_iter().collect()),
 9638                        ..Default::default()
 9639                    },
 9640                )]
 9641                .into_iter()
 9642                .collect(),
 9643                ..Default::default()
 9644            },
 9645            Some(tree_sitter_typescript::language_tsx()),
 9646        )
 9647        .with_override_query("(jsx_self_closing_element) @element")
 9648        .unwrap(),
 9649        lsp::ServerCapabilities {
 9650            completion_provider: Some(lsp::CompletionOptions {
 9651                trigger_characters: Some(vec![":".to_string()]),
 9652                ..Default::default()
 9653            }),
 9654            ..Default::default()
 9655        },
 9656        cx,
 9657    )
 9658    .await;
 9659
 9660    cx.lsp
 9661        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9662            Ok(Some(lsp::CompletionResponse::Array(vec![
 9663                lsp::CompletionItem {
 9664                    label: "bg-blue".into(),
 9665                    ..Default::default()
 9666                },
 9667                lsp::CompletionItem {
 9668                    label: "bg-red".into(),
 9669                    ..Default::default()
 9670                },
 9671                lsp::CompletionItem {
 9672                    label: "bg-yellow".into(),
 9673                    ..Default::default()
 9674                },
 9675            ])))
 9676        });
 9677
 9678    cx.set_state(r#"<p class="bgˇ" />"#);
 9679
 9680    // Trigger completion when typing a dash, because the dash is an extra
 9681    // word character in the 'element' scope, which contains the cursor.
 9682    cx.simulate_keystroke("-");
 9683    cx.executor().run_until_parked();
 9684    cx.update_editor(|editor, _| {
 9685        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9686            assert_eq!(
 9687                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9688                &["bg-red", "bg-blue", "bg-yellow"]
 9689            );
 9690        } else {
 9691            panic!("expected completion menu to be open");
 9692        }
 9693    });
 9694
 9695    cx.simulate_keystroke("l");
 9696    cx.executor().run_until_parked();
 9697    cx.update_editor(|editor, _| {
 9698        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9699            assert_eq!(
 9700                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9701                &["bg-blue", "bg-yellow"]
 9702            );
 9703        } else {
 9704            panic!("expected completion menu to be open");
 9705        }
 9706    });
 9707
 9708    // When filtering completions, consider the character after the '-' to
 9709    // be the start of a subword.
 9710    cx.set_state(r#"<p class="yelˇ" />"#);
 9711    cx.simulate_keystroke("l");
 9712    cx.executor().run_until_parked();
 9713    cx.update_editor(|editor, _| {
 9714        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9715            assert_eq!(
 9716                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9717                &["bg-yellow"]
 9718            );
 9719        } else {
 9720            panic!("expected completion menu to be open");
 9721        }
 9722    });
 9723}
 9724
 9725#[gpui::test]
 9726async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9727    init_test(cx, |settings| {
 9728        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9729            FormatterList(vec![Formatter::Prettier].into()),
 9730        ))
 9731    });
 9732
 9733    let fs = FakeFs::new(cx.executor());
 9734    fs.insert_file("/file.ts", Default::default()).await;
 9735
 9736    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9737    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9738
 9739    language_registry.add(Arc::new(Language::new(
 9740        LanguageConfig {
 9741            name: "TypeScript".into(),
 9742            matcher: LanguageMatcher {
 9743                path_suffixes: vec!["ts".to_string()],
 9744                ..Default::default()
 9745            },
 9746            ..Default::default()
 9747        },
 9748        Some(tree_sitter_rust::language()),
 9749    )));
 9750    update_test_language_settings(cx, |settings| {
 9751        settings.defaults.prettier = Some(PrettierSettings {
 9752            allowed: true,
 9753            ..PrettierSettings::default()
 9754        });
 9755    });
 9756
 9757    let test_plugin = "test_plugin";
 9758    let _ = language_registry.register_fake_lsp_adapter(
 9759        "TypeScript",
 9760        FakeLspAdapter {
 9761            prettier_plugins: vec![test_plugin],
 9762            ..Default::default()
 9763        },
 9764    );
 9765
 9766    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9767    let buffer = project
 9768        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9769        .await
 9770        .unwrap();
 9771
 9772    let buffer_text = "one\ntwo\nthree\n";
 9773    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9774    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9775    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9776
 9777    editor
 9778        .update(cx, |editor, cx| {
 9779            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9780        })
 9781        .unwrap()
 9782        .await;
 9783    assert_eq!(
 9784        editor.update(cx, |editor, cx| editor.text(cx)),
 9785        buffer_text.to_string() + prettier_format_suffix,
 9786        "Test prettier formatting was not applied to the original buffer text",
 9787    );
 9788
 9789    update_test_language_settings(cx, |settings| {
 9790        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9791    });
 9792    let format = editor.update(cx, |editor, cx| {
 9793        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9794    });
 9795    format.await.unwrap();
 9796    assert_eq!(
 9797        editor.update(cx, |editor, cx| editor.text(cx)),
 9798        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9799        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9800    );
 9801}
 9802
 9803#[gpui::test]
 9804async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9805    init_test(cx, |_| {});
 9806    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9807    let base_text = indoc! {r#"struct Row;
 9808struct Row1;
 9809struct Row2;
 9810
 9811struct Row4;
 9812struct Row5;
 9813struct Row6;
 9814
 9815struct Row8;
 9816struct Row9;
 9817struct Row10;"#};
 9818
 9819    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9820    assert_hunk_revert(
 9821        indoc! {r#"struct Row;
 9822                   struct Row1;
 9823                   struct Row1.1;
 9824                   struct Row1.2;
 9825                   struct Row2;ˇ
 9826
 9827                   struct Row4;
 9828                   struct Row5;
 9829                   struct Row6;
 9830
 9831                   struct Row8;
 9832                   ˇstruct Row9;
 9833                   struct Row9.1;
 9834                   struct Row9.2;
 9835                   struct Row9.3;
 9836                   struct Row10;"#},
 9837        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9838        indoc! {r#"struct Row;
 9839                   struct Row1;
 9840                   struct Row1.1;
 9841                   struct Row1.2;
 9842                   struct Row2;ˇ
 9843
 9844                   struct Row4;
 9845                   struct Row5;
 9846                   struct Row6;
 9847
 9848                   struct Row8;
 9849                   ˇstruct Row9;
 9850                   struct Row9.1;
 9851                   struct Row9.2;
 9852                   struct Row9.3;
 9853                   struct Row10;"#},
 9854        base_text,
 9855        &mut cx,
 9856    );
 9857    // Same for selections
 9858    assert_hunk_revert(
 9859        indoc! {r#"struct Row;
 9860                   struct Row1;
 9861                   struct Row2;
 9862                   struct Row2.1;
 9863                   struct Row2.2;
 9864                   «ˇ
 9865                   struct Row4;
 9866                   struct» Row5;
 9867                   «struct Row6;
 9868                   ˇ»
 9869                   struct Row9.1;
 9870                   struct Row9.2;
 9871                   struct Row9.3;
 9872                   struct Row8;
 9873                   struct Row9;
 9874                   struct Row10;"#},
 9875        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9876        indoc! {r#"struct Row;
 9877                   struct Row1;
 9878                   struct Row2;
 9879                   struct Row2.1;
 9880                   struct Row2.2;
 9881                   «ˇ
 9882                   struct Row4;
 9883                   struct» Row5;
 9884                   «struct Row6;
 9885                   ˇ»
 9886                   struct Row9.1;
 9887                   struct Row9.2;
 9888                   struct Row9.3;
 9889                   struct Row8;
 9890                   struct Row9;
 9891                   struct Row10;"#},
 9892        base_text,
 9893        &mut cx,
 9894    );
 9895
 9896    // When carets and selections intersect the addition hunks, those are reverted.
 9897    // Adjacent carets got merged.
 9898    assert_hunk_revert(
 9899        indoc! {r#"struct Row;
 9900                   ˇ// something on the top
 9901                   struct Row1;
 9902                   struct Row2;
 9903                   struct Roˇw3.1;
 9904                   struct Row2.2;
 9905                   struct Row2.3;ˇ
 9906
 9907                   struct Row4;
 9908                   struct ˇRow5.1;
 9909                   struct Row5.2;
 9910                   struct «Rowˇ»5.3;
 9911                   struct Row5;
 9912                   struct Row6;
 9913                   ˇ
 9914                   struct Row9.1;
 9915                   struct «Rowˇ»9.2;
 9916                   struct «ˇRow»9.3;
 9917                   struct Row8;
 9918                   struct Row9;
 9919                   «ˇ// something on bottom»
 9920                   struct Row10;"#},
 9921        vec![
 9922            DiffHunkStatus::Added,
 9923            DiffHunkStatus::Added,
 9924            DiffHunkStatus::Added,
 9925            DiffHunkStatus::Added,
 9926            DiffHunkStatus::Added,
 9927        ],
 9928        indoc! {r#"struct Row;
 9929                   ˇstruct Row1;
 9930                   struct Row2;
 9931                   ˇ
 9932                   struct Row4;
 9933                   ˇstruct Row5;
 9934                   struct Row6;
 9935                   ˇ
 9936                   ˇstruct Row8;
 9937                   struct Row9;
 9938                   ˇstruct Row10;"#},
 9939        base_text,
 9940        &mut cx,
 9941    );
 9942}
 9943
 9944#[gpui::test]
 9945async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9946    init_test(cx, |_| {});
 9947    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9948    let base_text = indoc! {r#"struct Row;
 9949struct Row1;
 9950struct Row2;
 9951
 9952struct Row4;
 9953struct Row5;
 9954struct Row6;
 9955
 9956struct Row8;
 9957struct Row9;
 9958struct Row10;"#};
 9959
 9960    // Modification hunks behave the same as the addition ones.
 9961    assert_hunk_revert(
 9962        indoc! {r#"struct Row;
 9963                   struct Row1;
 9964                   struct Row33;
 9965                   ˇ
 9966                   struct Row4;
 9967                   struct Row5;
 9968                   struct Row6;
 9969                   ˇ
 9970                   struct Row99;
 9971                   struct Row9;
 9972                   struct Row10;"#},
 9973        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9974        indoc! {r#"struct Row;
 9975                   struct Row1;
 9976                   struct Row33;
 9977                   ˇ
 9978                   struct Row4;
 9979                   struct Row5;
 9980                   struct Row6;
 9981                   ˇ
 9982                   struct Row99;
 9983                   struct Row9;
 9984                   struct Row10;"#},
 9985        base_text,
 9986        &mut cx,
 9987    );
 9988    assert_hunk_revert(
 9989        indoc! {r#"struct Row;
 9990                   struct Row1;
 9991                   struct Row33;
 9992                   «ˇ
 9993                   struct Row4;
 9994                   struct» Row5;
 9995                   «struct Row6;
 9996                   ˇ»
 9997                   struct Row99;
 9998                   struct Row9;
 9999                   struct Row10;"#},
10000        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10001        indoc! {r#"struct Row;
10002                   struct Row1;
10003                   struct Row33;
10004                   «ˇ
10005                   struct Row4;
10006                   struct» Row5;
10007                   «struct Row6;
10008                   ˇ»
10009                   struct Row99;
10010                   struct Row9;
10011                   struct Row10;"#},
10012        base_text,
10013        &mut cx,
10014    );
10015
10016    assert_hunk_revert(
10017        indoc! {r#"ˇstruct Row1.1;
10018                   struct Row1;
10019                   «ˇstr»uct Row22;
10020
10021                   struct ˇRow44;
10022                   struct Row5;
10023                   struct «Rˇ»ow66;ˇ
10024
10025                   «struˇ»ct Row88;
10026                   struct Row9;
10027                   struct Row1011;ˇ"#},
10028        vec![
10029            DiffHunkStatus::Modified,
10030            DiffHunkStatus::Modified,
10031            DiffHunkStatus::Modified,
10032            DiffHunkStatus::Modified,
10033            DiffHunkStatus::Modified,
10034            DiffHunkStatus::Modified,
10035        ],
10036        indoc! {r#"struct Row;
10037                   ˇstruct Row1;
10038                   struct Row2;
10039                   ˇ
10040                   struct Row4;
10041                   ˇstruct Row5;
10042                   struct Row6;
10043                   ˇ
10044                   struct Row8;
10045                   ˇstruct Row9;
10046                   struct Row10;ˇ"#},
10047        base_text,
10048        &mut cx,
10049    );
10050}
10051
10052#[gpui::test]
10053async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10054    init_test(cx, |_| {});
10055    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10056    let base_text = indoc! {r#"struct Row;
10057struct Row1;
10058struct Row2;
10059
10060struct Row4;
10061struct Row5;
10062struct Row6;
10063
10064struct Row8;
10065struct Row9;
10066struct Row10;"#};
10067
10068    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
10069    assert_hunk_revert(
10070        indoc! {r#"struct Row;
10071                   struct Row2;
10072
10073                   ˇstruct Row4;
10074                   struct Row5;
10075                   struct Row6;
10076                   ˇ
10077                   struct Row8;
10078                   struct Row10;"#},
10079        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10080        indoc! {r#"struct Row;
10081                   struct Row2;
10082
10083                   ˇstruct Row4;
10084                   struct Row5;
10085                   struct Row6;
10086                   ˇ
10087                   struct Row8;
10088                   struct Row10;"#},
10089        base_text,
10090        &mut cx,
10091    );
10092    assert_hunk_revert(
10093        indoc! {r#"struct Row;
10094                   struct Row2;
10095
10096                   «ˇstruct Row4;
10097                   struct» Row5;
10098                   «struct Row6;
10099                   ˇ»
10100                   struct Row8;
10101                   struct Row10;"#},
10102        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10103        indoc! {r#"struct Row;
10104                   struct Row2;
10105
10106                   «ˇstruct Row4;
10107                   struct» Row5;
10108                   «struct Row6;
10109                   ˇ»
10110                   struct Row8;
10111                   struct Row10;"#},
10112        base_text,
10113        &mut cx,
10114    );
10115
10116    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10117    assert_hunk_revert(
10118        indoc! {r#"struct Row;
10119                   ˇstruct Row2;
10120
10121                   struct Row4;
10122                   struct Row5;
10123                   struct Row6;
10124
10125                   struct Row8;ˇ
10126                   struct Row10;"#},
10127        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10128        indoc! {r#"struct Row;
10129                   struct Row1;
10130                   ˇstruct Row2;
10131
10132                   struct Row4;
10133                   struct Row5;
10134                   struct Row6;
10135
10136                   struct Row8;ˇ
10137                   struct Row9;
10138                   struct Row10;"#},
10139        base_text,
10140        &mut cx,
10141    );
10142    assert_hunk_revert(
10143        indoc! {r#"struct Row;
10144                   struct Row2«ˇ;
10145                   struct Row4;
10146                   struct» Row5;
10147                   «struct Row6;
10148
10149                   struct Row8;ˇ»
10150                   struct Row10;"#},
10151        vec![
10152            DiffHunkStatus::Removed,
10153            DiffHunkStatus::Removed,
10154            DiffHunkStatus::Removed,
10155        ],
10156        indoc! {r#"struct Row;
10157                   struct Row1;
10158                   struct Row2«ˇ;
10159
10160                   struct Row4;
10161                   struct» Row5;
10162                   «struct Row6;
10163
10164                   struct Row8;ˇ»
10165                   struct Row9;
10166                   struct Row10;"#},
10167        base_text,
10168        &mut cx,
10169    );
10170}
10171
10172#[gpui::test]
10173async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10174    init_test(cx, |_| {});
10175
10176    let cols = 4;
10177    let rows = 10;
10178    let sample_text_1 = sample_text(rows, cols, 'a');
10179    assert_eq!(
10180        sample_text_1,
10181        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10182    );
10183    let sample_text_2 = sample_text(rows, cols, 'l');
10184    assert_eq!(
10185        sample_text_2,
10186        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10187    );
10188    let sample_text_3 = sample_text(rows, cols, 'v');
10189    assert_eq!(
10190        sample_text_3,
10191        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10192    );
10193
10194    fn diff_every_buffer_row(
10195        buffer: &Model<Buffer>,
10196        sample_text: String,
10197        cols: usize,
10198        cx: &mut gpui::TestAppContext,
10199    ) {
10200        // revert first character in each row, creating one large diff hunk per buffer
10201        let is_first_char = |offset: usize| offset % cols == 0;
10202        buffer.update(cx, |buffer, cx| {
10203            buffer.set_text(
10204                sample_text
10205                    .chars()
10206                    .enumerate()
10207                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10208                    .collect::<String>(),
10209                cx,
10210            );
10211            buffer.set_diff_base(Some(sample_text), cx);
10212        });
10213        cx.executor().run_until_parked();
10214    }
10215
10216    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10217    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10218
10219    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10220    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10221
10222    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10223    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10224
10225    let multibuffer = cx.new_model(|cx| {
10226        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10227        multibuffer.push_excerpts(
10228            buffer_1.clone(),
10229            [
10230                ExcerptRange {
10231                    context: Point::new(0, 0)..Point::new(3, 0),
10232                    primary: None,
10233                },
10234                ExcerptRange {
10235                    context: Point::new(5, 0)..Point::new(7, 0),
10236                    primary: None,
10237                },
10238                ExcerptRange {
10239                    context: Point::new(9, 0)..Point::new(10, 4),
10240                    primary: None,
10241                },
10242            ],
10243            cx,
10244        );
10245        multibuffer.push_excerpts(
10246            buffer_2.clone(),
10247            [
10248                ExcerptRange {
10249                    context: Point::new(0, 0)..Point::new(3, 0),
10250                    primary: None,
10251                },
10252                ExcerptRange {
10253                    context: Point::new(5, 0)..Point::new(7, 0),
10254                    primary: None,
10255                },
10256                ExcerptRange {
10257                    context: Point::new(9, 0)..Point::new(10, 4),
10258                    primary: None,
10259                },
10260            ],
10261            cx,
10262        );
10263        multibuffer.push_excerpts(
10264            buffer_3.clone(),
10265            [
10266                ExcerptRange {
10267                    context: Point::new(0, 0)..Point::new(3, 0),
10268                    primary: None,
10269                },
10270                ExcerptRange {
10271                    context: Point::new(5, 0)..Point::new(7, 0),
10272                    primary: None,
10273                },
10274                ExcerptRange {
10275                    context: Point::new(9, 0)..Point::new(10, 4),
10276                    primary: None,
10277                },
10278            ],
10279            cx,
10280        );
10281        multibuffer
10282    });
10283
10284    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10285    editor.update(cx, |editor, cx| {
10286        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");
10287        editor.select_all(&SelectAll, cx);
10288        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10289    });
10290    cx.executor().run_until_parked();
10291    // When all ranges are selected, all buffer hunks are reverted.
10292    editor.update(cx, |editor, cx| {
10293        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");
10294    });
10295    buffer_1.update(cx, |buffer, _| {
10296        assert_eq!(buffer.text(), sample_text_1);
10297    });
10298    buffer_2.update(cx, |buffer, _| {
10299        assert_eq!(buffer.text(), sample_text_2);
10300    });
10301    buffer_3.update(cx, |buffer, _| {
10302        assert_eq!(buffer.text(), sample_text_3);
10303    });
10304
10305    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10306    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10307    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10308    editor.update(cx, |editor, cx| {
10309        editor.change_selections(None, cx, |s| {
10310            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10311        });
10312        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10313    });
10314    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10315    // but not affect buffer_2 and its related excerpts.
10316    editor.update(cx, |editor, cx| {
10317        assert_eq!(
10318            editor.text(cx),
10319            "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"
10320        );
10321    });
10322    buffer_1.update(cx, |buffer, _| {
10323        assert_eq!(buffer.text(), sample_text_1);
10324    });
10325    buffer_2.update(cx, |buffer, _| {
10326        assert_eq!(
10327            buffer.text(),
10328            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10329        );
10330    });
10331    buffer_3.update(cx, |buffer, _| {
10332        assert_eq!(
10333            buffer.text(),
10334            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10335        );
10336    });
10337}
10338
10339#[gpui::test]
10340async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10341    init_test(cx, |_| {});
10342
10343    let cols = 4;
10344    let rows = 10;
10345    let sample_text_1 = sample_text(rows, cols, 'a');
10346    assert_eq!(
10347        sample_text_1,
10348        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10349    );
10350    let sample_text_2 = sample_text(rows, cols, 'l');
10351    assert_eq!(
10352        sample_text_2,
10353        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10354    );
10355    let sample_text_3 = sample_text(rows, cols, 'v');
10356    assert_eq!(
10357        sample_text_3,
10358        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10359    );
10360
10361    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10362    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10363    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10364
10365    let multi_buffer = cx.new_model(|cx| {
10366        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10367        multibuffer.push_excerpts(
10368            buffer_1.clone(),
10369            [
10370                ExcerptRange {
10371                    context: Point::new(0, 0)..Point::new(3, 0),
10372                    primary: None,
10373                },
10374                ExcerptRange {
10375                    context: Point::new(5, 0)..Point::new(7, 0),
10376                    primary: None,
10377                },
10378                ExcerptRange {
10379                    context: Point::new(9, 0)..Point::new(10, 4),
10380                    primary: None,
10381                },
10382            ],
10383            cx,
10384        );
10385        multibuffer.push_excerpts(
10386            buffer_2.clone(),
10387            [
10388                ExcerptRange {
10389                    context: Point::new(0, 0)..Point::new(3, 0),
10390                    primary: None,
10391                },
10392                ExcerptRange {
10393                    context: Point::new(5, 0)..Point::new(7, 0),
10394                    primary: None,
10395                },
10396                ExcerptRange {
10397                    context: Point::new(9, 0)..Point::new(10, 4),
10398                    primary: None,
10399                },
10400            ],
10401            cx,
10402        );
10403        multibuffer.push_excerpts(
10404            buffer_3.clone(),
10405            [
10406                ExcerptRange {
10407                    context: Point::new(0, 0)..Point::new(3, 0),
10408                    primary: None,
10409                },
10410                ExcerptRange {
10411                    context: Point::new(5, 0)..Point::new(7, 0),
10412                    primary: None,
10413                },
10414                ExcerptRange {
10415                    context: Point::new(9, 0)..Point::new(10, 4),
10416                    primary: None,
10417                },
10418            ],
10419            cx,
10420        );
10421        multibuffer
10422    });
10423
10424    let fs = FakeFs::new(cx.executor());
10425    fs.insert_tree(
10426        "/a",
10427        json!({
10428            "main.rs": sample_text_1,
10429            "other.rs": sample_text_2,
10430            "lib.rs": sample_text_3,
10431        }),
10432    )
10433    .await;
10434    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10435    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10436    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10437    let multi_buffer_editor = cx.new_view(|cx| {
10438        Editor::new(
10439            EditorMode::Full,
10440            multi_buffer,
10441            Some(project.clone()),
10442            true,
10443            cx,
10444        )
10445    });
10446    let multibuffer_item_id = workspace
10447        .update(cx, |workspace, cx| {
10448            assert!(
10449                workspace.active_item(cx).is_none(),
10450                "active item should be None before the first item is added"
10451            );
10452            workspace.add_item_to_active_pane(
10453                Box::new(multi_buffer_editor.clone()),
10454                None,
10455                true,
10456                cx,
10457            );
10458            let active_item = workspace
10459                .active_item(cx)
10460                .expect("should have an active item after adding the multi buffer");
10461            assert!(
10462                !active_item.is_singleton(cx),
10463                "A multi buffer was expected to active after adding"
10464            );
10465            active_item.item_id()
10466        })
10467        .unwrap();
10468    cx.executor().run_until_parked();
10469
10470    multi_buffer_editor.update(cx, |editor, cx| {
10471        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10472        editor.open_excerpts(&OpenExcerpts, cx);
10473    });
10474    cx.executor().run_until_parked();
10475    let first_item_id = workspace
10476        .update(cx, |workspace, cx| {
10477            let active_item = workspace
10478                .active_item(cx)
10479                .expect("should have an active item after navigating into the 1st buffer");
10480            let first_item_id = active_item.item_id();
10481            assert_ne!(
10482                first_item_id, multibuffer_item_id,
10483                "Should navigate into the 1st buffer and activate it"
10484            );
10485            assert!(
10486                active_item.is_singleton(cx),
10487                "New active item should be a singleton buffer"
10488            );
10489            assert_eq!(
10490                active_item
10491                    .act_as::<Editor>(cx)
10492                    .expect("should have navigated into an editor for the 1st buffer")
10493                    .read(cx)
10494                    .text(cx),
10495                sample_text_1
10496            );
10497
10498            workspace
10499                .go_back(workspace.active_pane().downgrade(), cx)
10500                .detach_and_log_err(cx);
10501
10502            first_item_id
10503        })
10504        .unwrap();
10505    cx.executor().run_until_parked();
10506    workspace
10507        .update(cx, |workspace, cx| {
10508            let active_item = workspace
10509                .active_item(cx)
10510                .expect("should have an active item after navigating back");
10511            assert_eq!(
10512                active_item.item_id(),
10513                multibuffer_item_id,
10514                "Should navigate back to the multi buffer"
10515            );
10516            assert!(!active_item.is_singleton(cx));
10517        })
10518        .unwrap();
10519
10520    multi_buffer_editor.update(cx, |editor, cx| {
10521        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10522            s.select_ranges(Some(39..40))
10523        });
10524        editor.open_excerpts(&OpenExcerpts, cx);
10525    });
10526    cx.executor().run_until_parked();
10527    let second_item_id = workspace
10528        .update(cx, |workspace, cx| {
10529            let active_item = workspace
10530                .active_item(cx)
10531                .expect("should have an active item after navigating into the 2nd buffer");
10532            let second_item_id = active_item.item_id();
10533            assert_ne!(
10534                second_item_id, multibuffer_item_id,
10535                "Should navigate away from the multibuffer"
10536            );
10537            assert_ne!(
10538                second_item_id, first_item_id,
10539                "Should navigate into the 2nd buffer and activate it"
10540            );
10541            assert!(
10542                active_item.is_singleton(cx),
10543                "New active item should be a singleton buffer"
10544            );
10545            assert_eq!(
10546                active_item
10547                    .act_as::<Editor>(cx)
10548                    .expect("should have navigated into an editor")
10549                    .read(cx)
10550                    .text(cx),
10551                sample_text_2
10552            );
10553
10554            workspace
10555                .go_back(workspace.active_pane().downgrade(), cx)
10556                .detach_and_log_err(cx);
10557
10558            second_item_id
10559        })
10560        .unwrap();
10561    cx.executor().run_until_parked();
10562    workspace
10563        .update(cx, |workspace, cx| {
10564            let active_item = workspace
10565                .active_item(cx)
10566                .expect("should have an active item after navigating back from the 2nd buffer");
10567            assert_eq!(
10568                active_item.item_id(),
10569                multibuffer_item_id,
10570                "Should navigate back from the 2nd buffer to the multi buffer"
10571            );
10572            assert!(!active_item.is_singleton(cx));
10573        })
10574        .unwrap();
10575
10576    multi_buffer_editor.update(cx, |editor, cx| {
10577        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10578            s.select_ranges(Some(60..70))
10579        });
10580        editor.open_excerpts(&OpenExcerpts, cx);
10581    });
10582    cx.executor().run_until_parked();
10583    workspace
10584        .update(cx, |workspace, cx| {
10585            let active_item = workspace
10586                .active_item(cx)
10587                .expect("should have an active item after navigating into the 3rd buffer");
10588            let third_item_id = active_item.item_id();
10589            assert_ne!(
10590                third_item_id, multibuffer_item_id,
10591                "Should navigate into the 3rd buffer and activate it"
10592            );
10593            assert_ne!(third_item_id, first_item_id);
10594            assert_ne!(third_item_id, second_item_id);
10595            assert!(
10596                active_item.is_singleton(cx),
10597                "New active item should be a singleton buffer"
10598            );
10599            assert_eq!(
10600                active_item
10601                    .act_as::<Editor>(cx)
10602                    .expect("should have navigated into an editor")
10603                    .read(cx)
10604                    .text(cx),
10605                sample_text_3
10606            );
10607
10608            workspace
10609                .go_back(workspace.active_pane().downgrade(), cx)
10610                .detach_and_log_err(cx);
10611        })
10612        .unwrap();
10613    cx.executor().run_until_parked();
10614    workspace
10615        .update(cx, |workspace, cx| {
10616            let active_item = workspace
10617                .active_item(cx)
10618                .expect("should have an active item after navigating back from the 3rd buffer");
10619            assert_eq!(
10620                active_item.item_id(),
10621                multibuffer_item_id,
10622                "Should navigate back from the 3rd buffer to the multi buffer"
10623            );
10624            assert!(!active_item.is_singleton(cx));
10625        })
10626        .unwrap();
10627}
10628
10629#[gpui::test]
10630async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10631    init_test(cx, |_| {});
10632
10633    let mut cx = EditorTestContext::new(cx).await;
10634
10635    let diff_base = r#"
10636        use some::mod;
10637
10638        const A: u32 = 42;
10639
10640        fn main() {
10641            println!("hello");
10642
10643            println!("world");
10644        }
10645        "#
10646    .unindent();
10647
10648    cx.set_state(
10649        &r#"
10650        use some::modified;
10651
10652        ˇ
10653        fn main() {
10654            println!("hello there");
10655
10656            println!("around the");
10657            println!("world");
10658        }
10659        "#
10660        .unindent(),
10661    );
10662
10663    cx.set_diff_base(Some(&diff_base));
10664    executor.run_until_parked();
10665    let unexpanded_hunks = vec![
10666        (
10667            "use some::mod;\n".to_string(),
10668            DiffHunkStatus::Modified,
10669            DisplayRow(0)..DisplayRow(1),
10670        ),
10671        (
10672            "const A: u32 = 42;\n".to_string(),
10673            DiffHunkStatus::Removed,
10674            DisplayRow(2)..DisplayRow(2),
10675        ),
10676        (
10677            "    println!(\"hello\");\n".to_string(),
10678            DiffHunkStatus::Modified,
10679            DisplayRow(4)..DisplayRow(5),
10680        ),
10681        (
10682            "".to_string(),
10683            DiffHunkStatus::Added,
10684            DisplayRow(6)..DisplayRow(7),
10685        ),
10686    ];
10687    cx.update_editor(|editor, cx| {
10688        let snapshot = editor.snapshot(cx);
10689        let all_hunks = editor_hunks(editor, &snapshot, cx);
10690        assert_eq!(all_hunks, unexpanded_hunks);
10691    });
10692
10693    cx.update_editor(|editor, cx| {
10694        for _ in 0..4 {
10695            editor.go_to_hunk(&GoToHunk, cx);
10696            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10697        }
10698    });
10699    executor.run_until_parked();
10700    cx.assert_editor_state(
10701        &r#"
10702        use some::modified;
10703
10704        ˇ
10705        fn main() {
10706            println!("hello there");
10707
10708            println!("around the");
10709            println!("world");
10710        }
10711        "#
10712        .unindent(),
10713    );
10714    cx.update_editor(|editor, cx| {
10715        let snapshot = editor.snapshot(cx);
10716        let all_hunks = editor_hunks(editor, &snapshot, cx);
10717        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10718        assert_eq!(
10719            expanded_hunks_background_highlights(editor, cx),
10720            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10721            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10722        );
10723        assert_eq!(
10724            all_hunks,
10725            vec![
10726                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10727                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10728                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10729                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10730            ],
10731            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10732            (from modified and removed hunks)"
10733        );
10734        assert_eq!(
10735            all_hunks, all_expanded_hunks,
10736            "Editor hunks should not change and all be expanded"
10737        );
10738    });
10739
10740    cx.update_editor(|editor, cx| {
10741        editor.cancel(&Cancel, cx);
10742
10743        let snapshot = editor.snapshot(cx);
10744        let all_hunks = editor_hunks(editor, &snapshot, cx);
10745        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10746        assert_eq!(
10747            expanded_hunks_background_highlights(editor, cx),
10748            Vec::new(),
10749            "After cancelling in editor, no git highlights should be left"
10750        );
10751        assert_eq!(
10752            all_expanded_hunks,
10753            Vec::new(),
10754            "After cancelling in editor, no hunks should be expanded"
10755        );
10756        assert_eq!(
10757            all_hunks, unexpanded_hunks,
10758            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10759        );
10760    });
10761}
10762
10763#[gpui::test]
10764async fn test_toggled_diff_base_change(
10765    executor: BackgroundExecutor,
10766    cx: &mut gpui::TestAppContext,
10767) {
10768    init_test(cx, |_| {});
10769
10770    let mut cx = EditorTestContext::new(cx).await;
10771
10772    let diff_base = r#"
10773        use some::mod1;
10774        use some::mod2;
10775
10776        const A: u32 = 42;
10777        const B: u32 = 42;
10778        const C: u32 = 42;
10779
10780        fn main(ˇ) {
10781            println!("hello");
10782
10783            println!("world");
10784        }
10785        "#
10786    .unindent();
10787
10788    cx.set_state(
10789        &r#"
10790        use some::mod2;
10791
10792        const A: u32 = 42;
10793        const C: u32 = 42;
10794
10795        fn main(ˇ) {
10796            //println!("hello");
10797
10798            println!("world");
10799            //
10800            //
10801        }
10802        "#
10803        .unindent(),
10804    );
10805
10806    cx.set_diff_base(Some(&diff_base));
10807    executor.run_until_parked();
10808    cx.update_editor(|editor, cx| {
10809        let snapshot = editor.snapshot(cx);
10810        let all_hunks = editor_hunks(editor, &snapshot, cx);
10811        assert_eq!(
10812            all_hunks,
10813            vec![
10814                (
10815                    "use some::mod1;\n".to_string(),
10816                    DiffHunkStatus::Removed,
10817                    DisplayRow(0)..DisplayRow(0)
10818                ),
10819                (
10820                    "const B: u32 = 42;\n".to_string(),
10821                    DiffHunkStatus::Removed,
10822                    DisplayRow(3)..DisplayRow(3)
10823                ),
10824                (
10825                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10826                    DiffHunkStatus::Modified,
10827                    DisplayRow(5)..DisplayRow(7)
10828                ),
10829                (
10830                    "".to_string(),
10831                    DiffHunkStatus::Added,
10832                    DisplayRow(9)..DisplayRow(11)
10833                ),
10834            ]
10835        );
10836    });
10837
10838    cx.update_editor(|editor, cx| {
10839        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10840    });
10841    executor.run_until_parked();
10842    cx.assert_editor_state(
10843        &r#"
10844        use some::mod2;
10845
10846        const A: u32 = 42;
10847        const C: u32 = 42;
10848
10849        fn main(ˇ) {
10850            //println!("hello");
10851
10852            println!("world");
10853            //
10854            //
10855        }
10856        "#
10857        .unindent(),
10858    );
10859    cx.update_editor(|editor, cx| {
10860        let snapshot = editor.snapshot(cx);
10861        let all_hunks = editor_hunks(editor, &snapshot, cx);
10862        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10863        assert_eq!(
10864            expanded_hunks_background_highlights(editor, cx),
10865            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10866            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10867        );
10868        assert_eq!(
10869            all_hunks,
10870            vec![
10871                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10872                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10873                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10874                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10875            ],
10876            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10877            (from modified and removed hunks)"
10878        );
10879        assert_eq!(
10880            all_hunks, all_expanded_hunks,
10881            "Editor hunks should not change and all be expanded"
10882        );
10883    });
10884
10885    cx.set_diff_base(Some("new diff base!"));
10886    executor.run_until_parked();
10887
10888    cx.update_editor(|editor, cx| {
10889        let snapshot = editor.snapshot(cx);
10890        let all_hunks = editor_hunks(editor, &snapshot, cx);
10891        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10892        assert_eq!(
10893            expanded_hunks_background_highlights(editor, cx),
10894            Vec::new(),
10895            "After diff base is changed, old git highlights should be removed"
10896        );
10897        assert_eq!(
10898            all_expanded_hunks,
10899            Vec::new(),
10900            "After diff base is changed, old git hunk expansions should be removed"
10901        );
10902        assert_eq!(
10903            all_hunks,
10904            vec![(
10905                "new diff base!".to_string(),
10906                DiffHunkStatus::Modified,
10907                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10908            )],
10909            "After diff base is changed, hunks should update"
10910        );
10911    });
10912}
10913
10914#[gpui::test]
10915async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10916    init_test(cx, |_| {});
10917
10918    let mut cx = EditorTestContext::new(cx).await;
10919
10920    let diff_base = r#"
10921        use some::mod1;
10922        use some::mod2;
10923
10924        const A: u32 = 42;
10925        const B: u32 = 42;
10926        const C: u32 = 42;
10927
10928        fn main(ˇ) {
10929            println!("hello");
10930
10931            println!("world");
10932        }
10933
10934        fn another() {
10935            println!("another");
10936        }
10937
10938        fn another2() {
10939            println!("another2");
10940        }
10941        "#
10942    .unindent();
10943
10944    cx.set_state(
10945        &r#"
10946        «use some::mod2;
10947
10948        const A: u32 = 42;
10949        const C: u32 = 42;
10950
10951        fn main() {
10952            //println!("hello");
10953
10954            println!("world");
10955            //
10956            //ˇ»
10957        }
10958
10959        fn another() {
10960            println!("another");
10961            println!("another");
10962        }
10963
10964            println!("another2");
10965        }
10966        "#
10967        .unindent(),
10968    );
10969
10970    cx.set_diff_base(Some(&diff_base));
10971    executor.run_until_parked();
10972    cx.update_editor(|editor, cx| {
10973        let snapshot = editor.snapshot(cx);
10974        let all_hunks = editor_hunks(editor, &snapshot, cx);
10975        assert_eq!(
10976            all_hunks,
10977            vec![
10978                (
10979                    "use some::mod1;\n".to_string(),
10980                    DiffHunkStatus::Removed,
10981                    DisplayRow(0)..DisplayRow(0)
10982                ),
10983                (
10984                    "const B: u32 = 42;\n".to_string(),
10985                    DiffHunkStatus::Removed,
10986                    DisplayRow(3)..DisplayRow(3)
10987                ),
10988                (
10989                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10990                    DiffHunkStatus::Modified,
10991                    DisplayRow(5)..DisplayRow(7)
10992                ),
10993                (
10994                    "".to_string(),
10995                    DiffHunkStatus::Added,
10996                    DisplayRow(9)..DisplayRow(11)
10997                ),
10998                (
10999                    "".to_string(),
11000                    DiffHunkStatus::Added,
11001                    DisplayRow(15)..DisplayRow(16)
11002                ),
11003                (
11004                    "fn another2() {\n".to_string(),
11005                    DiffHunkStatus::Removed,
11006                    DisplayRow(18)..DisplayRow(18)
11007                ),
11008            ]
11009        );
11010    });
11011
11012    cx.update_editor(|editor, cx| {
11013        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11014    });
11015    executor.run_until_parked();
11016    cx.assert_editor_state(
11017        &r#"
11018        «use some::mod2;
11019
11020        const A: u32 = 42;
11021        const C: u32 = 42;
11022
11023        fn main() {
11024            //println!("hello");
11025
11026            println!("world");
11027            //
11028            //ˇ»
11029        }
11030
11031        fn another() {
11032            println!("another");
11033            println!("another");
11034        }
11035
11036            println!("another2");
11037        }
11038        "#
11039        .unindent(),
11040    );
11041    cx.update_editor(|editor, cx| {
11042        let snapshot = editor.snapshot(cx);
11043        let all_hunks = editor_hunks(editor, &snapshot, cx);
11044        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11045        assert_eq!(
11046            expanded_hunks_background_highlights(editor, cx),
11047            vec![
11048                DisplayRow(9)..=DisplayRow(10),
11049                DisplayRow(13)..=DisplayRow(14),
11050                DisplayRow(19)..=DisplayRow(19)
11051            ]
11052        );
11053        assert_eq!(
11054            all_hunks,
11055            vec![
11056                (
11057                    "use some::mod1;\n".to_string(),
11058                    DiffHunkStatus::Removed,
11059                    DisplayRow(1)..DisplayRow(1)
11060                ),
11061                (
11062                    "const B: u32 = 42;\n".to_string(),
11063                    DiffHunkStatus::Removed,
11064                    DisplayRow(5)..DisplayRow(5)
11065                ),
11066                (
11067                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11068                    DiffHunkStatus::Modified,
11069                    DisplayRow(9)..DisplayRow(11)
11070                ),
11071                (
11072                    "".to_string(),
11073                    DiffHunkStatus::Added,
11074                    DisplayRow(13)..DisplayRow(15)
11075                ),
11076                (
11077                    "".to_string(),
11078                    DiffHunkStatus::Added,
11079                    DisplayRow(19)..DisplayRow(20)
11080                ),
11081                (
11082                    "fn another2() {\n".to_string(),
11083                    DiffHunkStatus::Removed,
11084                    DisplayRow(23)..DisplayRow(23)
11085                ),
11086            ],
11087        );
11088        assert_eq!(all_hunks, all_expanded_hunks);
11089    });
11090
11091    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11092    cx.executor().run_until_parked();
11093    cx.assert_editor_state(
11094        &r#"
11095        «use some::mod2;
11096
11097        const A: u32 = 42;
11098        const C: u32 = 42;
11099
11100        fn main() {
11101            //println!("hello");
11102
11103            println!("world");
11104            //
11105            //ˇ»
11106        }
11107
11108        fn another() {
11109            println!("another");
11110            println!("another");
11111        }
11112
11113            println!("another2");
11114        }
11115        "#
11116        .unindent(),
11117    );
11118    cx.update_editor(|editor, cx| {
11119        let snapshot = editor.snapshot(cx);
11120        let all_hunks = editor_hunks(editor, &snapshot, cx);
11121        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11122        assert_eq!(
11123            expanded_hunks_background_highlights(editor, cx),
11124            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11125            "Only one hunk is left not folded, its highlight should be visible"
11126        );
11127        assert_eq!(
11128            all_hunks,
11129            vec![
11130                (
11131                    "use some::mod1;\n".to_string(),
11132                    DiffHunkStatus::Removed,
11133                    DisplayRow(0)..DisplayRow(0)
11134                ),
11135                (
11136                    "const B: u32 = 42;\n".to_string(),
11137                    DiffHunkStatus::Removed,
11138                    DisplayRow(0)..DisplayRow(0)
11139                ),
11140                (
11141                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11142                    DiffHunkStatus::Modified,
11143                    DisplayRow(0)..DisplayRow(0)
11144                ),
11145                (
11146                    "".to_string(),
11147                    DiffHunkStatus::Added,
11148                    DisplayRow(0)..DisplayRow(1)
11149                ),
11150                (
11151                    "".to_string(),
11152                    DiffHunkStatus::Added,
11153                    DisplayRow(5)..DisplayRow(6)
11154                ),
11155                (
11156                    "fn another2() {\n".to_string(),
11157                    DiffHunkStatus::Removed,
11158                    DisplayRow(9)..DisplayRow(9)
11159                ),
11160            ],
11161            "Hunk list should still return shifted folded hunks"
11162        );
11163        assert_eq!(
11164            all_expanded_hunks,
11165            vec![
11166                (
11167                    "".to_string(),
11168                    DiffHunkStatus::Added,
11169                    DisplayRow(5)..DisplayRow(6)
11170                ),
11171                (
11172                    "fn another2() {\n".to_string(),
11173                    DiffHunkStatus::Removed,
11174                    DisplayRow(9)..DisplayRow(9)
11175                ),
11176            ],
11177            "Only non-folded hunks should be left expanded"
11178        );
11179    });
11180
11181    cx.update_editor(|editor, cx| {
11182        editor.select_all(&SelectAll, cx);
11183        editor.unfold_lines(&UnfoldLines, cx);
11184    });
11185    cx.executor().run_until_parked();
11186    cx.assert_editor_state(
11187        &r#"
11188        «use some::mod2;
11189
11190        const A: u32 = 42;
11191        const C: u32 = 42;
11192
11193        fn main() {
11194            //println!("hello");
11195
11196            println!("world");
11197            //
11198            //
11199        }
11200
11201        fn another() {
11202            println!("another");
11203            println!("another");
11204        }
11205
11206            println!("another2");
11207        }
11208        ˇ»"#
11209        .unindent(),
11210    );
11211    cx.update_editor(|editor, cx| {
11212        let snapshot = editor.snapshot(cx);
11213        let all_hunks = editor_hunks(editor, &snapshot, cx);
11214        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11215        assert_eq!(
11216            expanded_hunks_background_highlights(editor, cx),
11217            vec![
11218                DisplayRow(9)..=DisplayRow(10),
11219                DisplayRow(13)..=DisplayRow(14),
11220                DisplayRow(19)..=DisplayRow(19)
11221            ],
11222            "After unfolding, all hunk diffs should be visible again"
11223        );
11224        assert_eq!(
11225            all_hunks,
11226            vec![
11227                (
11228                    "use some::mod1;\n".to_string(),
11229                    DiffHunkStatus::Removed,
11230                    DisplayRow(1)..DisplayRow(1)
11231                ),
11232                (
11233                    "const B: u32 = 42;\n".to_string(),
11234                    DiffHunkStatus::Removed,
11235                    DisplayRow(5)..DisplayRow(5)
11236                ),
11237                (
11238                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11239                    DiffHunkStatus::Modified,
11240                    DisplayRow(9)..DisplayRow(11)
11241                ),
11242                (
11243                    "".to_string(),
11244                    DiffHunkStatus::Added,
11245                    DisplayRow(13)..DisplayRow(15)
11246                ),
11247                (
11248                    "".to_string(),
11249                    DiffHunkStatus::Added,
11250                    DisplayRow(19)..DisplayRow(20)
11251                ),
11252                (
11253                    "fn another2() {\n".to_string(),
11254                    DiffHunkStatus::Removed,
11255                    DisplayRow(23)..DisplayRow(23)
11256                ),
11257            ],
11258        );
11259        assert_eq!(all_hunks, all_expanded_hunks);
11260    });
11261}
11262
11263#[gpui::test]
11264async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11265    init_test(cx, |_| {});
11266
11267    let cols = 4;
11268    let rows = 10;
11269    let sample_text_1 = sample_text(rows, cols, 'a');
11270    assert_eq!(
11271        sample_text_1,
11272        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11273    );
11274    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11275    let sample_text_2 = sample_text(rows, cols, 'l');
11276    assert_eq!(
11277        sample_text_2,
11278        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11279    );
11280    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11281    let sample_text_3 = sample_text(rows, cols, 'v');
11282    assert_eq!(
11283        sample_text_3,
11284        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11285    );
11286    let modified_sample_text_3 =
11287        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11288    let buffer_1 = cx.new_model(|cx| {
11289        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11290        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11291        buffer
11292    });
11293    let buffer_2 = cx.new_model(|cx| {
11294        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11295        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11296        buffer
11297    });
11298    let buffer_3 = cx.new_model(|cx| {
11299        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11300        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11301        buffer
11302    });
11303
11304    let multi_buffer = cx.new_model(|cx| {
11305        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11306        multibuffer.push_excerpts(
11307            buffer_1.clone(),
11308            [
11309                ExcerptRange {
11310                    context: Point::new(0, 0)..Point::new(3, 0),
11311                    primary: None,
11312                },
11313                ExcerptRange {
11314                    context: Point::new(5, 0)..Point::new(7, 0),
11315                    primary: None,
11316                },
11317                ExcerptRange {
11318                    context: Point::new(9, 0)..Point::new(10, 4),
11319                    primary: None,
11320                },
11321            ],
11322            cx,
11323        );
11324        multibuffer.push_excerpts(
11325            buffer_2.clone(),
11326            [
11327                ExcerptRange {
11328                    context: Point::new(0, 0)..Point::new(3, 0),
11329                    primary: None,
11330                },
11331                ExcerptRange {
11332                    context: Point::new(5, 0)..Point::new(7, 0),
11333                    primary: None,
11334                },
11335                ExcerptRange {
11336                    context: Point::new(9, 0)..Point::new(10, 4),
11337                    primary: None,
11338                },
11339            ],
11340            cx,
11341        );
11342        multibuffer.push_excerpts(
11343            buffer_3.clone(),
11344            [
11345                ExcerptRange {
11346                    context: Point::new(0, 0)..Point::new(3, 0),
11347                    primary: None,
11348                },
11349                ExcerptRange {
11350                    context: Point::new(5, 0)..Point::new(7, 0),
11351                    primary: None,
11352                },
11353                ExcerptRange {
11354                    context: Point::new(9, 0)..Point::new(10, 4),
11355                    primary: None,
11356                },
11357            ],
11358            cx,
11359        );
11360        multibuffer
11361    });
11362
11363    let fs = FakeFs::new(cx.executor());
11364    fs.insert_tree(
11365        "/a",
11366        json!({
11367            "main.rs": modified_sample_text_1,
11368            "other.rs": modified_sample_text_2,
11369            "lib.rs": modified_sample_text_3,
11370        }),
11371    )
11372    .await;
11373
11374    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11375    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11376    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11377    let multi_buffer_editor = cx.new_view(|cx| {
11378        Editor::new(
11379            EditorMode::Full,
11380            multi_buffer,
11381            Some(project.clone()),
11382            true,
11383            cx,
11384        )
11385    });
11386    cx.executor().run_until_parked();
11387
11388    let expected_all_hunks = vec![
11389        (
11390            "bbbb\n".to_string(),
11391            DiffHunkStatus::Removed,
11392            DisplayRow(4)..DisplayRow(4),
11393        ),
11394        (
11395            "nnnn\n".to_string(),
11396            DiffHunkStatus::Modified,
11397            DisplayRow(21)..DisplayRow(22),
11398        ),
11399        (
11400            "".to_string(),
11401            DiffHunkStatus::Added,
11402            DisplayRow(41)..DisplayRow(42),
11403        ),
11404    ];
11405    let expected_all_hunks_shifted = vec![
11406        (
11407            "bbbb\n".to_string(),
11408            DiffHunkStatus::Removed,
11409            DisplayRow(5)..DisplayRow(5),
11410        ),
11411        (
11412            "nnnn\n".to_string(),
11413            DiffHunkStatus::Modified,
11414            DisplayRow(23)..DisplayRow(24),
11415        ),
11416        (
11417            "".to_string(),
11418            DiffHunkStatus::Added,
11419            DisplayRow(43)..DisplayRow(44),
11420        ),
11421    ];
11422
11423    multi_buffer_editor.update(cx, |editor, cx| {
11424        let snapshot = editor.snapshot(cx);
11425        let all_hunks = editor_hunks(editor, &snapshot, cx);
11426        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11427        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11428        assert_eq!(all_hunks, expected_all_hunks);
11429        assert_eq!(all_expanded_hunks, Vec::new());
11430    });
11431
11432    multi_buffer_editor.update(cx, |editor, cx| {
11433        editor.select_all(&SelectAll, cx);
11434        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11435    });
11436    cx.executor().run_until_parked();
11437    multi_buffer_editor.update(cx, |editor, cx| {
11438        let snapshot = editor.snapshot(cx);
11439        let all_hunks = editor_hunks(editor, &snapshot, cx);
11440        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11441        assert_eq!(
11442            expanded_hunks_background_highlights(editor, cx),
11443            vec![
11444                DisplayRow(23)..=DisplayRow(23),
11445                DisplayRow(43)..=DisplayRow(43)
11446            ],
11447        );
11448        assert_eq!(all_hunks, expected_all_hunks_shifted);
11449        assert_eq!(all_hunks, all_expanded_hunks);
11450    });
11451
11452    multi_buffer_editor.update(cx, |editor, cx| {
11453        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11454    });
11455    cx.executor().run_until_parked();
11456    multi_buffer_editor.update(cx, |editor, cx| {
11457        let snapshot = editor.snapshot(cx);
11458        let all_hunks = editor_hunks(editor, &snapshot, cx);
11459        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11460        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11461        assert_eq!(all_hunks, expected_all_hunks);
11462        assert_eq!(all_expanded_hunks, Vec::new());
11463    });
11464
11465    multi_buffer_editor.update(cx, |editor, cx| {
11466        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11467    });
11468    cx.executor().run_until_parked();
11469    multi_buffer_editor.update(cx, |editor, cx| {
11470        let snapshot = editor.snapshot(cx);
11471        let all_hunks = editor_hunks(editor, &snapshot, cx);
11472        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11473        assert_eq!(
11474            expanded_hunks_background_highlights(editor, cx),
11475            vec![
11476                DisplayRow(23)..=DisplayRow(23),
11477                DisplayRow(43)..=DisplayRow(43)
11478            ],
11479        );
11480        assert_eq!(all_hunks, expected_all_hunks_shifted);
11481        assert_eq!(all_hunks, all_expanded_hunks);
11482    });
11483
11484    multi_buffer_editor.update(cx, |editor, cx| {
11485        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11486    });
11487    cx.executor().run_until_parked();
11488    multi_buffer_editor.update(cx, |editor, cx| {
11489        let snapshot = editor.snapshot(cx);
11490        let all_hunks = editor_hunks(editor, &snapshot, cx);
11491        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11492        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11493        assert_eq!(all_hunks, expected_all_hunks);
11494        assert_eq!(all_expanded_hunks, Vec::new());
11495    });
11496}
11497
11498#[gpui::test]
11499async fn test_edits_around_toggled_additions(
11500    executor: BackgroundExecutor,
11501    cx: &mut gpui::TestAppContext,
11502) {
11503    init_test(cx, |_| {});
11504
11505    let mut cx = EditorTestContext::new(cx).await;
11506
11507    let diff_base = r#"
11508        use some::mod1;
11509        use some::mod2;
11510
11511        const A: u32 = 42;
11512
11513        fn main() {
11514            println!("hello");
11515
11516            println!("world");
11517        }
11518        "#
11519    .unindent();
11520    executor.run_until_parked();
11521    cx.set_state(
11522        &r#"
11523        use some::mod1;
11524        use some::mod2;
11525
11526        const A: u32 = 42;
11527        const B: u32 = 42;
11528        const C: u32 = 42;
11529        ˇ
11530
11531        fn main() {
11532            println!("hello");
11533
11534            println!("world");
11535        }
11536        "#
11537        .unindent(),
11538    );
11539
11540    cx.set_diff_base(Some(&diff_base));
11541    executor.run_until_parked();
11542    cx.update_editor(|editor, cx| {
11543        let snapshot = editor.snapshot(cx);
11544        let all_hunks = editor_hunks(editor, &snapshot, cx);
11545        assert_eq!(
11546            all_hunks,
11547            vec![(
11548                "".to_string(),
11549                DiffHunkStatus::Added,
11550                DisplayRow(4)..DisplayRow(7)
11551            )]
11552        );
11553    });
11554    cx.update_editor(|editor, cx| {
11555        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11556    });
11557    executor.run_until_parked();
11558    cx.assert_editor_state(
11559        &r#"
11560        use some::mod1;
11561        use some::mod2;
11562
11563        const A: u32 = 42;
11564        const B: u32 = 42;
11565        const C: u32 = 42;
11566        ˇ
11567
11568        fn main() {
11569            println!("hello");
11570
11571            println!("world");
11572        }
11573        "#
11574        .unindent(),
11575    );
11576    cx.update_editor(|editor, cx| {
11577        let snapshot = editor.snapshot(cx);
11578        let all_hunks = editor_hunks(editor, &snapshot, cx);
11579        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11580        assert_eq!(
11581            all_hunks,
11582            vec![(
11583                "".to_string(),
11584                DiffHunkStatus::Added,
11585                DisplayRow(4)..DisplayRow(7)
11586            )]
11587        );
11588        assert_eq!(
11589            expanded_hunks_background_highlights(editor, cx),
11590            vec![DisplayRow(4)..=DisplayRow(6)]
11591        );
11592        assert_eq!(all_hunks, all_expanded_hunks);
11593    });
11594
11595    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11596    executor.run_until_parked();
11597    cx.assert_editor_state(
11598        &r#"
11599        use some::mod1;
11600        use some::mod2;
11601
11602        const A: u32 = 42;
11603        const B: u32 = 42;
11604        const C: u32 = 42;
11605        const D: u32 = 42;
11606        ˇ
11607
11608        fn main() {
11609            println!("hello");
11610
11611            println!("world");
11612        }
11613        "#
11614        .unindent(),
11615    );
11616    cx.update_editor(|editor, cx| {
11617        let snapshot = editor.snapshot(cx);
11618        let all_hunks = editor_hunks(editor, &snapshot, cx);
11619        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11620        assert_eq!(
11621            all_hunks,
11622            vec![(
11623                "".to_string(),
11624                DiffHunkStatus::Added,
11625                DisplayRow(4)..DisplayRow(8)
11626            )]
11627        );
11628        assert_eq!(
11629            expanded_hunks_background_highlights(editor, cx),
11630            vec![DisplayRow(4)..=DisplayRow(6)],
11631            "Edited hunk should have one more line added"
11632        );
11633        assert_eq!(
11634            all_hunks, all_expanded_hunks,
11635            "Expanded hunk should also grow with the addition"
11636        );
11637    });
11638
11639    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11640    executor.run_until_parked();
11641    cx.assert_editor_state(
11642        &r#"
11643        use some::mod1;
11644        use some::mod2;
11645
11646        const A: u32 = 42;
11647        const B: u32 = 42;
11648        const C: u32 = 42;
11649        const D: u32 = 42;
11650        const E: u32 = 42;
11651        ˇ
11652
11653        fn main() {
11654            println!("hello");
11655
11656            println!("world");
11657        }
11658        "#
11659        .unindent(),
11660    );
11661    cx.update_editor(|editor, cx| {
11662        let snapshot = editor.snapshot(cx);
11663        let all_hunks = editor_hunks(editor, &snapshot, cx);
11664        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11665        assert_eq!(
11666            all_hunks,
11667            vec![(
11668                "".to_string(),
11669                DiffHunkStatus::Added,
11670                DisplayRow(4)..DisplayRow(9)
11671            )]
11672        );
11673        assert_eq!(
11674            expanded_hunks_background_highlights(editor, cx),
11675            vec![DisplayRow(4)..=DisplayRow(6)],
11676            "Edited hunk should have one more line added"
11677        );
11678        assert_eq!(all_hunks, all_expanded_hunks);
11679    });
11680
11681    cx.update_editor(|editor, cx| {
11682        editor.move_up(&MoveUp, cx);
11683        editor.delete_line(&DeleteLine, cx);
11684    });
11685    executor.run_until_parked();
11686    cx.assert_editor_state(
11687        &r#"
11688        use some::mod1;
11689        use some::mod2;
11690
11691        const A: u32 = 42;
11692        const B: u32 = 42;
11693        const C: u32 = 42;
11694        const D: u32 = 42;
11695        ˇ
11696
11697        fn main() {
11698            println!("hello");
11699
11700            println!("world");
11701        }
11702        "#
11703        .unindent(),
11704    );
11705    cx.update_editor(|editor, cx| {
11706        let snapshot = editor.snapshot(cx);
11707        let all_hunks = editor_hunks(editor, &snapshot, cx);
11708        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11709        assert_eq!(
11710            all_hunks,
11711            vec![(
11712                "".to_string(),
11713                DiffHunkStatus::Added,
11714                DisplayRow(4)..DisplayRow(8)
11715            )]
11716        );
11717        assert_eq!(
11718            expanded_hunks_background_highlights(editor, cx),
11719            vec![DisplayRow(4)..=DisplayRow(6)],
11720            "Deleting a line should shrint the hunk"
11721        );
11722        assert_eq!(
11723            all_hunks, all_expanded_hunks,
11724            "Expanded hunk should also shrink with the addition"
11725        );
11726    });
11727
11728    cx.update_editor(|editor, cx| {
11729        editor.move_up(&MoveUp, cx);
11730        editor.delete_line(&DeleteLine, cx);
11731        editor.move_up(&MoveUp, cx);
11732        editor.delete_line(&DeleteLine, cx);
11733        editor.move_up(&MoveUp, cx);
11734        editor.delete_line(&DeleteLine, cx);
11735    });
11736    executor.run_until_parked();
11737    cx.assert_editor_state(
11738        &r#"
11739        use some::mod1;
11740        use some::mod2;
11741
11742        const A: u32 = 42;
11743        ˇ
11744
11745        fn main() {
11746            println!("hello");
11747
11748            println!("world");
11749        }
11750        "#
11751        .unindent(),
11752    );
11753    cx.update_editor(|editor, cx| {
11754        let snapshot = editor.snapshot(cx);
11755        let all_hunks = editor_hunks(editor, &snapshot, cx);
11756        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11757        assert_eq!(
11758            all_hunks,
11759            vec![(
11760                "".to_string(),
11761                DiffHunkStatus::Added,
11762                DisplayRow(5)..DisplayRow(6)
11763            )]
11764        );
11765        assert_eq!(
11766            expanded_hunks_background_highlights(editor, cx),
11767            vec![DisplayRow(5)..=DisplayRow(5)]
11768        );
11769        assert_eq!(all_hunks, all_expanded_hunks);
11770    });
11771
11772    cx.update_editor(|editor, cx| {
11773        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11774        editor.delete_line(&DeleteLine, cx);
11775    });
11776    executor.run_until_parked();
11777    cx.assert_editor_state(
11778        &r#"
11779        ˇ
11780
11781        fn main() {
11782            println!("hello");
11783
11784            println!("world");
11785        }
11786        "#
11787        .unindent(),
11788    );
11789    cx.update_editor(|editor, cx| {
11790        let snapshot = editor.snapshot(cx);
11791        let all_hunks = editor_hunks(editor, &snapshot, cx);
11792        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11793        assert_eq!(
11794            all_hunks,
11795            vec![
11796                (
11797                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11798                    DiffHunkStatus::Removed,
11799                    DisplayRow(0)..DisplayRow(0)
11800                ),
11801                (
11802                    "const A: u32 = 42;\n".to_string(),
11803                    DiffHunkStatus::Removed,
11804                    DisplayRow(2)..DisplayRow(2)
11805                )
11806            ]
11807        );
11808        assert_eq!(
11809            expanded_hunks_background_highlights(editor, cx),
11810            Vec::new(),
11811            "Should close all stale expanded addition hunks"
11812        );
11813        assert_eq!(
11814            all_expanded_hunks,
11815            vec![(
11816                "const A: u32 = 42;\n".to_string(),
11817                DiffHunkStatus::Removed,
11818                DisplayRow(2)..DisplayRow(2)
11819            )],
11820            "Should open hunks that were adjacent to the stale addition one"
11821        );
11822    });
11823}
11824
11825#[gpui::test]
11826async fn test_edits_around_toggled_deletions(
11827    executor: BackgroundExecutor,
11828    cx: &mut gpui::TestAppContext,
11829) {
11830    init_test(cx, |_| {});
11831
11832    let mut cx = EditorTestContext::new(cx).await;
11833
11834    let diff_base = r#"
11835        use some::mod1;
11836        use some::mod2;
11837
11838        const A: u32 = 42;
11839        const B: u32 = 42;
11840        const C: u32 = 42;
11841
11842
11843        fn main() {
11844            println!("hello");
11845
11846            println!("world");
11847        }
11848        "#
11849    .unindent();
11850    executor.run_until_parked();
11851    cx.set_state(
11852        &r#"
11853        use some::mod1;
11854        use some::mod2;
11855
11856        ˇconst B: u32 = 42;
11857        const C: u32 = 42;
11858
11859
11860        fn main() {
11861            println!("hello");
11862
11863            println!("world");
11864        }
11865        "#
11866        .unindent(),
11867    );
11868
11869    cx.set_diff_base(Some(&diff_base));
11870    executor.run_until_parked();
11871    cx.update_editor(|editor, cx| {
11872        let snapshot = editor.snapshot(cx);
11873        let all_hunks = editor_hunks(editor, &snapshot, cx);
11874        assert_eq!(
11875            all_hunks,
11876            vec![(
11877                "const A: u32 = 42;\n".to_string(),
11878                DiffHunkStatus::Removed,
11879                DisplayRow(3)..DisplayRow(3)
11880            )]
11881        );
11882    });
11883    cx.update_editor(|editor, cx| {
11884        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11885    });
11886    executor.run_until_parked();
11887    cx.assert_editor_state(
11888        &r#"
11889        use some::mod1;
11890        use some::mod2;
11891
11892        ˇconst B: u32 = 42;
11893        const C: u32 = 42;
11894
11895
11896        fn main() {
11897            println!("hello");
11898
11899            println!("world");
11900        }
11901        "#
11902        .unindent(),
11903    );
11904    cx.update_editor(|editor, cx| {
11905        let snapshot = editor.snapshot(cx);
11906        let all_hunks = editor_hunks(editor, &snapshot, cx);
11907        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11908        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11909        assert_eq!(
11910            all_hunks,
11911            vec![(
11912                "const A: u32 = 42;\n".to_string(),
11913                DiffHunkStatus::Removed,
11914                DisplayRow(4)..DisplayRow(4)
11915            )]
11916        );
11917        assert_eq!(all_hunks, all_expanded_hunks);
11918    });
11919
11920    cx.update_editor(|editor, cx| {
11921        editor.delete_line(&DeleteLine, cx);
11922    });
11923    executor.run_until_parked();
11924    cx.assert_editor_state(
11925        &r#"
11926        use some::mod1;
11927        use some::mod2;
11928
11929        ˇconst C: u32 = 42;
11930
11931
11932        fn main() {
11933            println!("hello");
11934
11935            println!("world");
11936        }
11937        "#
11938        .unindent(),
11939    );
11940    cx.update_editor(|editor, cx| {
11941        let snapshot = editor.snapshot(cx);
11942        let all_hunks = editor_hunks(editor, &snapshot, cx);
11943        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11944        assert_eq!(
11945            expanded_hunks_background_highlights(editor, cx),
11946            Vec::new(),
11947            "Deleted hunks do not highlight current editor's background"
11948        );
11949        assert_eq!(
11950            all_hunks,
11951            vec![(
11952                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11953                DiffHunkStatus::Removed,
11954                DisplayRow(5)..DisplayRow(5)
11955            )]
11956        );
11957        assert_eq!(all_hunks, all_expanded_hunks);
11958    });
11959
11960    cx.update_editor(|editor, cx| {
11961        editor.delete_line(&DeleteLine, cx);
11962    });
11963    executor.run_until_parked();
11964    cx.assert_editor_state(
11965        &r#"
11966        use some::mod1;
11967        use some::mod2;
11968
11969        ˇ
11970
11971        fn main() {
11972            println!("hello");
11973
11974            println!("world");
11975        }
11976        "#
11977        .unindent(),
11978    );
11979    cx.update_editor(|editor, cx| {
11980        let snapshot = editor.snapshot(cx);
11981        let all_hunks = editor_hunks(editor, &snapshot, cx);
11982        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11983        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11984        assert_eq!(
11985            all_hunks,
11986            vec![(
11987                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11988                DiffHunkStatus::Removed,
11989                DisplayRow(6)..DisplayRow(6)
11990            )]
11991        );
11992        assert_eq!(all_hunks, all_expanded_hunks);
11993    });
11994
11995    cx.update_editor(|editor, cx| {
11996        editor.handle_input("replacement", cx);
11997    });
11998    executor.run_until_parked();
11999    cx.assert_editor_state(
12000        &r#"
12001        use some::mod1;
12002        use some::mod2;
12003
12004        replacementˇ
12005
12006        fn main() {
12007            println!("hello");
12008
12009            println!("world");
12010        }
12011        "#
12012        .unindent(),
12013    );
12014    cx.update_editor(|editor, cx| {
12015        let snapshot = editor.snapshot(cx);
12016        let all_hunks = editor_hunks(editor, &snapshot, cx);
12017        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12018        assert_eq!(
12019            all_hunks,
12020            vec![(
12021                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12022                DiffHunkStatus::Modified,
12023                DisplayRow(7)..DisplayRow(8)
12024            )]
12025        );
12026        assert_eq!(
12027            expanded_hunks_background_highlights(editor, cx),
12028            vec![DisplayRow(7)..=DisplayRow(7)],
12029            "Modified expanded hunks should display additions and highlight their background"
12030        );
12031        assert_eq!(all_hunks, all_expanded_hunks);
12032    });
12033}
12034
12035#[gpui::test]
12036async fn test_edits_around_toggled_modifications(
12037    executor: BackgroundExecutor,
12038    cx: &mut gpui::TestAppContext,
12039) {
12040    init_test(cx, |_| {});
12041
12042    let mut cx = EditorTestContext::new(cx).await;
12043
12044    let diff_base = r#"
12045        use some::mod1;
12046        use some::mod2;
12047
12048        const A: u32 = 42;
12049        const B: u32 = 42;
12050        const C: u32 = 42;
12051        const D: u32 = 42;
12052
12053
12054        fn main() {
12055            println!("hello");
12056
12057            println!("world");
12058        }"#
12059    .unindent();
12060    executor.run_until_parked();
12061    cx.set_state(
12062        &r#"
12063        use some::mod1;
12064        use some::mod2;
12065
12066        const A: u32 = 42;
12067        const B: u32 = 42;
12068        const C: u32 = 43ˇ
12069        const D: u32 = 42;
12070
12071
12072        fn main() {
12073            println!("hello");
12074
12075            println!("world");
12076        }"#
12077        .unindent(),
12078    );
12079
12080    cx.set_diff_base(Some(&diff_base));
12081    executor.run_until_parked();
12082    cx.update_editor(|editor, cx| {
12083        let snapshot = editor.snapshot(cx);
12084        let all_hunks = editor_hunks(editor, &snapshot, cx);
12085        assert_eq!(
12086            all_hunks,
12087            vec![(
12088                "const C: u32 = 42;\n".to_string(),
12089                DiffHunkStatus::Modified,
12090                DisplayRow(5)..DisplayRow(6)
12091            )]
12092        );
12093    });
12094    cx.update_editor(|editor, cx| {
12095        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12096    });
12097    executor.run_until_parked();
12098    cx.assert_editor_state(
12099        &r#"
12100        use some::mod1;
12101        use some::mod2;
12102
12103        const A: u32 = 42;
12104        const B: u32 = 42;
12105        const C: u32 = 43ˇ
12106        const D: u32 = 42;
12107
12108
12109        fn main() {
12110            println!("hello");
12111
12112            println!("world");
12113        }"#
12114        .unindent(),
12115    );
12116    cx.update_editor(|editor, cx| {
12117        let snapshot = editor.snapshot(cx);
12118        let all_hunks = editor_hunks(editor, &snapshot, cx);
12119        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12120        assert_eq!(
12121            expanded_hunks_background_highlights(editor, cx),
12122            vec![DisplayRow(6)..=DisplayRow(6)],
12123        );
12124        assert_eq!(
12125            all_hunks,
12126            vec![(
12127                "const C: u32 = 42;\n".to_string(),
12128                DiffHunkStatus::Modified,
12129                DisplayRow(6)..DisplayRow(7)
12130            )]
12131        );
12132        assert_eq!(all_hunks, all_expanded_hunks);
12133    });
12134
12135    cx.update_editor(|editor, cx| {
12136        editor.handle_input("\nnew_line\n", cx);
12137    });
12138    executor.run_until_parked();
12139    cx.assert_editor_state(
12140        &r#"
12141            use some::mod1;
12142            use some::mod2;
12143
12144            const A: u32 = 42;
12145            const B: u32 = 42;
12146            const C: u32 = 43
12147            new_line
12148            ˇ
12149            const D: u32 = 42;
12150
12151
12152            fn main() {
12153                println!("hello");
12154
12155                println!("world");
12156            }"#
12157        .unindent(),
12158    );
12159    cx.update_editor(|editor, cx| {
12160        let snapshot = editor.snapshot(cx);
12161        let all_hunks = editor_hunks(editor, &snapshot, cx);
12162        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12163        assert_eq!(
12164            expanded_hunks_background_highlights(editor, cx),
12165            vec![DisplayRow(6)..=DisplayRow(6)],
12166            "Modified hunk should grow highlighted lines on more text additions"
12167        );
12168        assert_eq!(
12169            all_hunks,
12170            vec![(
12171                "const C: u32 = 42;\n".to_string(),
12172                DiffHunkStatus::Modified,
12173                DisplayRow(6)..DisplayRow(9)
12174            )]
12175        );
12176        assert_eq!(all_hunks, all_expanded_hunks);
12177    });
12178
12179    cx.update_editor(|editor, cx| {
12180        editor.move_up(&MoveUp, cx);
12181        editor.move_up(&MoveUp, cx);
12182        editor.move_up(&MoveUp, cx);
12183        editor.delete_line(&DeleteLine, cx);
12184    });
12185    executor.run_until_parked();
12186    cx.assert_editor_state(
12187        &r#"
12188            use some::mod1;
12189            use some::mod2;
12190
12191            const A: u32 = 42;
12192            ˇconst C: u32 = 43
12193            new_line
12194
12195            const D: u32 = 42;
12196
12197
12198            fn main() {
12199                println!("hello");
12200
12201                println!("world");
12202            }"#
12203        .unindent(),
12204    );
12205    cx.update_editor(|editor, cx| {
12206        let snapshot = editor.snapshot(cx);
12207        let all_hunks = editor_hunks(editor, &snapshot, cx);
12208        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12209        assert_eq!(
12210            expanded_hunks_background_highlights(editor, cx),
12211            vec![DisplayRow(6)..=DisplayRow(8)],
12212        );
12213        assert_eq!(
12214            all_hunks,
12215            vec![(
12216                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12217                DiffHunkStatus::Modified,
12218                DisplayRow(6)..DisplayRow(9)
12219            )],
12220            "Modified hunk should grow deleted lines on text deletions above"
12221        );
12222        assert_eq!(all_hunks, all_expanded_hunks);
12223    });
12224
12225    cx.update_editor(|editor, cx| {
12226        editor.move_up(&MoveUp, cx);
12227        editor.handle_input("v", cx);
12228    });
12229    executor.run_until_parked();
12230    cx.assert_editor_state(
12231        &r#"
12232            use some::mod1;
12233            use some::mod2;
12234
12235            vˇconst A: u32 = 42;
12236            const C: u32 = 43
12237            new_line
12238
12239            const D: u32 = 42;
12240
12241
12242            fn main() {
12243                println!("hello");
12244
12245                println!("world");
12246            }"#
12247        .unindent(),
12248    );
12249    cx.update_editor(|editor, cx| {
12250        let snapshot = editor.snapshot(cx);
12251        let all_hunks = editor_hunks(editor, &snapshot, cx);
12252        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12253        assert_eq!(
12254            expanded_hunks_background_highlights(editor, cx),
12255            vec![DisplayRow(6)..=DisplayRow(9)],
12256            "Modified hunk should grow deleted lines on text modifications above"
12257        );
12258        assert_eq!(
12259            all_hunks,
12260            vec![(
12261                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12262                DiffHunkStatus::Modified,
12263                DisplayRow(6)..DisplayRow(10)
12264            )]
12265        );
12266        assert_eq!(all_hunks, all_expanded_hunks);
12267    });
12268
12269    cx.update_editor(|editor, cx| {
12270        editor.move_down(&MoveDown, cx);
12271        editor.move_down(&MoveDown, cx);
12272        editor.delete_line(&DeleteLine, cx)
12273    });
12274    executor.run_until_parked();
12275    cx.assert_editor_state(
12276        &r#"
12277            use some::mod1;
12278            use some::mod2;
12279
12280            vconst A: u32 = 42;
12281            const C: u32 = 43
12282            ˇ
12283            const D: u32 = 42;
12284
12285
12286            fn main() {
12287                println!("hello");
12288
12289                println!("world");
12290            }"#
12291        .unindent(),
12292    );
12293    cx.update_editor(|editor, cx| {
12294        let snapshot = editor.snapshot(cx);
12295        let all_hunks = editor_hunks(editor, &snapshot, cx);
12296        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12297        assert_eq!(
12298            expanded_hunks_background_highlights(editor, cx),
12299            vec![DisplayRow(6)..=DisplayRow(8)],
12300            "Modified hunk should grow shrink lines on modification lines removal"
12301        );
12302        assert_eq!(
12303            all_hunks,
12304            vec![(
12305                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12306                DiffHunkStatus::Modified,
12307                DisplayRow(6)..DisplayRow(9)
12308            )]
12309        );
12310        assert_eq!(all_hunks, all_expanded_hunks);
12311    });
12312
12313    cx.update_editor(|editor, cx| {
12314        editor.move_up(&MoveUp, cx);
12315        editor.move_up(&MoveUp, cx);
12316        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12317        editor.delete_line(&DeleteLine, cx)
12318    });
12319    executor.run_until_parked();
12320    cx.assert_editor_state(
12321        &r#"
12322            use some::mod1;
12323            use some::mod2;
12324
12325            ˇ
12326
12327            fn main() {
12328                println!("hello");
12329
12330                println!("world");
12331            }"#
12332        .unindent(),
12333    );
12334    cx.update_editor(|editor, cx| {
12335        let snapshot = editor.snapshot(cx);
12336        let all_hunks = editor_hunks(editor, &snapshot, cx);
12337        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12338        assert_eq!(
12339            expanded_hunks_background_highlights(editor, cx),
12340            Vec::new(),
12341            "Modified hunk should turn into a removed one on all modified lines removal"
12342        );
12343        assert_eq!(
12344            all_hunks,
12345            vec![(
12346                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12347                    .to_string(),
12348                DiffHunkStatus::Removed,
12349                DisplayRow(7)..DisplayRow(7)
12350            )]
12351        );
12352        assert_eq!(all_hunks, all_expanded_hunks);
12353    });
12354}
12355
12356#[gpui::test]
12357async fn test_multiple_expanded_hunks_merge(
12358    executor: BackgroundExecutor,
12359    cx: &mut gpui::TestAppContext,
12360) {
12361    init_test(cx, |_| {});
12362
12363    let mut cx = EditorTestContext::new(cx).await;
12364
12365    let diff_base = r#"
12366        use some::mod1;
12367        use some::mod2;
12368
12369        const A: u32 = 42;
12370        const B: u32 = 42;
12371        const C: u32 = 42;
12372        const D: u32 = 42;
12373
12374
12375        fn main() {
12376            println!("hello");
12377
12378            println!("world");
12379        }"#
12380    .unindent();
12381    executor.run_until_parked();
12382    cx.set_state(
12383        &r#"
12384        use some::mod1;
12385        use some::mod2;
12386
12387        const A: u32 = 42;
12388        const B: u32 = 42;
12389        const C: u32 = 43ˇ
12390        const D: u32 = 42;
12391
12392
12393        fn main() {
12394            println!("hello");
12395
12396            println!("world");
12397        }"#
12398        .unindent(),
12399    );
12400
12401    cx.set_diff_base(Some(&diff_base));
12402    executor.run_until_parked();
12403    cx.update_editor(|editor, cx| {
12404        let snapshot = editor.snapshot(cx);
12405        let all_hunks = editor_hunks(editor, &snapshot, cx);
12406        assert_eq!(
12407            all_hunks,
12408            vec![(
12409                "const C: u32 = 42;\n".to_string(),
12410                DiffHunkStatus::Modified,
12411                DisplayRow(5)..DisplayRow(6)
12412            )]
12413        );
12414    });
12415    cx.update_editor(|editor, cx| {
12416        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12417    });
12418    executor.run_until_parked();
12419    cx.assert_editor_state(
12420        &r#"
12421        use some::mod1;
12422        use some::mod2;
12423
12424        const A: u32 = 42;
12425        const B: u32 = 42;
12426        const C: u32 = 43ˇ
12427        const D: u32 = 42;
12428
12429
12430        fn main() {
12431            println!("hello");
12432
12433            println!("world");
12434        }"#
12435        .unindent(),
12436    );
12437    cx.update_editor(|editor, cx| {
12438        let snapshot = editor.snapshot(cx);
12439        let all_hunks = editor_hunks(editor, &snapshot, cx);
12440        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12441        assert_eq!(
12442            expanded_hunks_background_highlights(editor, cx),
12443            vec![DisplayRow(6)..=DisplayRow(6)],
12444        );
12445        assert_eq!(
12446            all_hunks,
12447            vec![(
12448                "const C: u32 = 42;\n".to_string(),
12449                DiffHunkStatus::Modified,
12450                DisplayRow(6)..DisplayRow(7)
12451            )]
12452        );
12453        assert_eq!(all_hunks, all_expanded_hunks);
12454    });
12455
12456    cx.update_editor(|editor, cx| {
12457        editor.handle_input("\nnew_line\n", cx);
12458    });
12459    executor.run_until_parked();
12460    cx.assert_editor_state(
12461        &r#"
12462            use some::mod1;
12463            use some::mod2;
12464
12465            const A: u32 = 42;
12466            const B: u32 = 42;
12467            const C: u32 = 43
12468            new_line
12469            ˇ
12470            const D: u32 = 42;
12471
12472
12473            fn main() {
12474                println!("hello");
12475
12476                println!("world");
12477            }"#
12478        .unindent(),
12479    );
12480}
12481
12482async fn setup_indent_guides_editor(
12483    text: &str,
12484    cx: &mut gpui::TestAppContext,
12485) -> (BufferId, EditorTestContext) {
12486    init_test(cx, |_| {});
12487
12488    let mut cx = EditorTestContext::new(cx).await;
12489
12490    let buffer_id = cx.update_editor(|editor, cx| {
12491        editor.set_text(text, cx);
12492        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12493        let buffer_id = buffer_ids[0];
12494        buffer_id
12495    });
12496
12497    (buffer_id, cx)
12498}
12499
12500fn assert_indent_guides(
12501    range: Range<u32>,
12502    expected: Vec<IndentGuide>,
12503    active_indices: Option<Vec<usize>>,
12504    cx: &mut EditorTestContext,
12505) {
12506    let indent_guides = cx.update_editor(|editor, cx| {
12507        let snapshot = editor.snapshot(cx).display_snapshot;
12508        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12509            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12510            true,
12511            &snapshot,
12512            cx,
12513        );
12514
12515        indent_guides.sort_by(|a, b| {
12516            a.depth.cmp(&b.depth).then(
12517                a.start_row
12518                    .cmp(&b.start_row)
12519                    .then(a.end_row.cmp(&b.end_row)),
12520            )
12521        });
12522        indent_guides
12523    });
12524
12525    if let Some(expected) = active_indices {
12526        let active_indices = cx.update_editor(|editor, cx| {
12527            let snapshot = editor.snapshot(cx).display_snapshot;
12528            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12529        });
12530
12531        assert_eq!(
12532            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12533            expected,
12534            "Active indent guide indices do not match"
12535        );
12536    }
12537
12538    let expected: Vec<_> = expected
12539        .into_iter()
12540        .map(|guide| MultiBufferIndentGuide {
12541            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12542            buffer: guide,
12543        })
12544        .collect();
12545
12546    assert_eq!(indent_guides, expected, "Indent guides do not match");
12547}
12548
12549fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12550    IndentGuide {
12551        buffer_id,
12552        start_row,
12553        end_row,
12554        depth,
12555        tab_size: 4,
12556        settings: IndentGuideSettings {
12557            enabled: true,
12558            line_width: 1,
12559            active_line_width: 1,
12560            ..Default::default()
12561        },
12562    }
12563}
12564
12565#[gpui::test]
12566async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12567    let (buffer_id, mut cx) = setup_indent_guides_editor(
12568        &"
12569    fn main() {
12570        let a = 1;
12571    }"
12572        .unindent(),
12573        cx,
12574    )
12575    .await;
12576
12577    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12578}
12579
12580#[gpui::test]
12581async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12582    let (buffer_id, mut cx) = setup_indent_guides_editor(
12583        &"
12584    fn main() {
12585        let a = 1;
12586        let b = 2;
12587    }"
12588        .unindent(),
12589        cx,
12590    )
12591    .await;
12592
12593    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12594}
12595
12596#[gpui::test]
12597async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12598    let (buffer_id, mut cx) = setup_indent_guides_editor(
12599        &"
12600    fn main() {
12601        let a = 1;
12602        if a == 3 {
12603            let b = 2;
12604        } else {
12605            let c = 3;
12606        }
12607    }"
12608        .unindent(),
12609        cx,
12610    )
12611    .await;
12612
12613    assert_indent_guides(
12614        0..8,
12615        vec![
12616            indent_guide(buffer_id, 1, 6, 0),
12617            indent_guide(buffer_id, 3, 3, 1),
12618            indent_guide(buffer_id, 5, 5, 1),
12619        ],
12620        None,
12621        &mut cx,
12622    );
12623}
12624
12625#[gpui::test]
12626async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12627    let (buffer_id, mut cx) = setup_indent_guides_editor(
12628        &"
12629    fn main() {
12630        let a = 1;
12631            let b = 2;
12632        let c = 3;
12633    }"
12634        .unindent(),
12635        cx,
12636    )
12637    .await;
12638
12639    assert_indent_guides(
12640        0..5,
12641        vec![
12642            indent_guide(buffer_id, 1, 3, 0),
12643            indent_guide(buffer_id, 2, 2, 1),
12644        ],
12645        None,
12646        &mut cx,
12647    );
12648}
12649
12650#[gpui::test]
12651async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12652    let (buffer_id, mut cx) = setup_indent_guides_editor(
12653        &"
12654        fn main() {
12655            let a = 1;
12656
12657            let c = 3;
12658        }"
12659        .unindent(),
12660        cx,
12661    )
12662    .await;
12663
12664    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12665}
12666
12667#[gpui::test]
12668async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12669    let (buffer_id, mut cx) = setup_indent_guides_editor(
12670        &"
12671        fn main() {
12672            let a = 1;
12673
12674            let c = 3;
12675
12676            if a == 3 {
12677                let b = 2;
12678            } else {
12679                let c = 3;
12680            }
12681        }"
12682        .unindent(),
12683        cx,
12684    )
12685    .await;
12686
12687    assert_indent_guides(
12688        0..11,
12689        vec![
12690            indent_guide(buffer_id, 1, 9, 0),
12691            indent_guide(buffer_id, 6, 6, 1),
12692            indent_guide(buffer_id, 8, 8, 1),
12693        ],
12694        None,
12695        &mut cx,
12696    );
12697}
12698
12699#[gpui::test]
12700async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12701    let (buffer_id, mut cx) = setup_indent_guides_editor(
12702        &"
12703        fn main() {
12704            let a = 1;
12705
12706            let c = 3;
12707
12708            if a == 3 {
12709                let b = 2;
12710            } else {
12711                let c = 3;
12712            }
12713        }"
12714        .unindent(),
12715        cx,
12716    )
12717    .await;
12718
12719    assert_indent_guides(
12720        1..11,
12721        vec![
12722            indent_guide(buffer_id, 1, 9, 0),
12723            indent_guide(buffer_id, 6, 6, 1),
12724            indent_guide(buffer_id, 8, 8, 1),
12725        ],
12726        None,
12727        &mut cx,
12728    );
12729}
12730
12731#[gpui::test]
12732async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12733    let (buffer_id, mut cx) = setup_indent_guides_editor(
12734        &"
12735        fn main() {
12736            let a = 1;
12737
12738            let c = 3;
12739
12740            if a == 3 {
12741                let b = 2;
12742            } else {
12743                let c = 3;
12744            }
12745        }"
12746        .unindent(),
12747        cx,
12748    )
12749    .await;
12750
12751    assert_indent_guides(
12752        1..10,
12753        vec![
12754            indent_guide(buffer_id, 1, 9, 0),
12755            indent_guide(buffer_id, 6, 6, 1),
12756            indent_guide(buffer_id, 8, 8, 1),
12757        ],
12758        None,
12759        &mut cx,
12760    );
12761}
12762
12763#[gpui::test]
12764async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12765    let (buffer_id, mut cx) = setup_indent_guides_editor(
12766        &"
12767        block1
12768            block2
12769                block3
12770                    block4
12771            block2
12772        block1
12773        block1"
12774            .unindent(),
12775        cx,
12776    )
12777    .await;
12778
12779    assert_indent_guides(
12780        1..10,
12781        vec![
12782            indent_guide(buffer_id, 1, 4, 0),
12783            indent_guide(buffer_id, 2, 3, 1),
12784            indent_guide(buffer_id, 3, 3, 2),
12785        ],
12786        None,
12787        &mut cx,
12788    );
12789}
12790
12791#[gpui::test]
12792async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12793    let (buffer_id, mut cx) = setup_indent_guides_editor(
12794        &"
12795        block1
12796            block2
12797                block3
12798
12799        block1
12800        block1"
12801            .unindent(),
12802        cx,
12803    )
12804    .await;
12805
12806    assert_indent_guides(
12807        0..6,
12808        vec![
12809            indent_guide(buffer_id, 1, 2, 0),
12810            indent_guide(buffer_id, 2, 2, 1),
12811        ],
12812        None,
12813        &mut cx,
12814    );
12815}
12816
12817#[gpui::test]
12818async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12819    let (buffer_id, mut cx) = setup_indent_guides_editor(
12820        &"
12821        block1
12822
12823
12824
12825            block2
12826        "
12827        .unindent(),
12828        cx,
12829    )
12830    .await;
12831
12832    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12833}
12834
12835#[gpui::test]
12836async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12837    let (buffer_id, mut cx) = setup_indent_guides_editor(
12838        &"
12839        def a:
12840        \tb = 3
12841        \tif True:
12842        \t\tc = 4
12843        \t\td = 5
12844        \tprint(b)
12845        "
12846        .unindent(),
12847        cx,
12848    )
12849    .await;
12850
12851    assert_indent_guides(
12852        0..6,
12853        vec![
12854            indent_guide(buffer_id, 1, 6, 0),
12855            indent_guide(buffer_id, 3, 4, 1),
12856        ],
12857        None,
12858        &mut cx,
12859    );
12860}
12861
12862#[gpui::test]
12863async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12864    let (buffer_id, mut cx) = setup_indent_guides_editor(
12865        &"
12866    fn main() {
12867        let a = 1;
12868    }"
12869        .unindent(),
12870        cx,
12871    )
12872    .await;
12873
12874    cx.update_editor(|editor, cx| {
12875        editor.change_selections(None, cx, |s| {
12876            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12877        });
12878    });
12879
12880    assert_indent_guides(
12881        0..3,
12882        vec![indent_guide(buffer_id, 1, 1, 0)],
12883        Some(vec![0]),
12884        &mut cx,
12885    );
12886}
12887
12888#[gpui::test]
12889async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12890    let (buffer_id, mut cx) = setup_indent_guides_editor(
12891        &"
12892    fn main() {
12893        if 1 == 2 {
12894            let a = 1;
12895        }
12896    }"
12897        .unindent(),
12898        cx,
12899    )
12900    .await;
12901
12902    cx.update_editor(|editor, cx| {
12903        editor.change_selections(None, cx, |s| {
12904            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12905        });
12906    });
12907
12908    assert_indent_guides(
12909        0..4,
12910        vec![
12911            indent_guide(buffer_id, 1, 3, 0),
12912            indent_guide(buffer_id, 2, 2, 1),
12913        ],
12914        Some(vec![1]),
12915        &mut cx,
12916    );
12917
12918    cx.update_editor(|editor, cx| {
12919        editor.change_selections(None, cx, |s| {
12920            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12921        });
12922    });
12923
12924    assert_indent_guides(
12925        0..4,
12926        vec![
12927            indent_guide(buffer_id, 1, 3, 0),
12928            indent_guide(buffer_id, 2, 2, 1),
12929        ],
12930        Some(vec![1]),
12931        &mut cx,
12932    );
12933
12934    cx.update_editor(|editor, cx| {
12935        editor.change_selections(None, cx, |s| {
12936            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12937        });
12938    });
12939
12940    assert_indent_guides(
12941        0..4,
12942        vec![
12943            indent_guide(buffer_id, 1, 3, 0),
12944            indent_guide(buffer_id, 2, 2, 1),
12945        ],
12946        Some(vec![0]),
12947        &mut cx,
12948    );
12949}
12950
12951#[gpui::test]
12952async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12953    let (buffer_id, mut cx) = setup_indent_guides_editor(
12954        &"
12955    fn main() {
12956        let a = 1;
12957
12958        let b = 2;
12959    }"
12960        .unindent(),
12961        cx,
12962    )
12963    .await;
12964
12965    cx.update_editor(|editor, cx| {
12966        editor.change_selections(None, cx, |s| {
12967            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12968        });
12969    });
12970
12971    assert_indent_guides(
12972        0..5,
12973        vec![indent_guide(buffer_id, 1, 3, 0)],
12974        Some(vec![0]),
12975        &mut cx,
12976    );
12977}
12978
12979#[gpui::test]
12980async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12981    let (buffer_id, mut cx) = setup_indent_guides_editor(
12982        &"
12983    def m:
12984        a = 1
12985        pass"
12986            .unindent(),
12987        cx,
12988    )
12989    .await;
12990
12991    cx.update_editor(|editor, cx| {
12992        editor.change_selections(None, cx, |s| {
12993            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12994        });
12995    });
12996
12997    assert_indent_guides(
12998        0..3,
12999        vec![indent_guide(buffer_id, 1, 2, 0)],
13000        Some(vec![0]),
13001        &mut cx,
13002    );
13003}
13004
13005#[gpui::test]
13006fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13007    init_test(cx, |_| {});
13008
13009    let editor = cx.add_window(|cx| {
13010        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13011        build_editor(buffer, cx)
13012    });
13013
13014    let render_args = Arc::new(Mutex::new(None));
13015    let snapshot = editor
13016        .update(cx, |editor, cx| {
13017            let snapshot = editor.buffer().read(cx).snapshot(cx);
13018            let range =
13019                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13020
13021            struct RenderArgs {
13022                row: MultiBufferRow,
13023                folded: bool,
13024                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13025            }
13026
13027            let crease = Crease::new(
13028                range,
13029                FoldPlaceholder::test(),
13030                {
13031                    let toggle_callback = render_args.clone();
13032                    move |row, folded, callback, _cx| {
13033                        *toggle_callback.lock() = Some(RenderArgs {
13034                            row,
13035                            folded,
13036                            callback,
13037                        });
13038                        div()
13039                    }
13040                },
13041                |_row, _folded, _cx| div(),
13042            );
13043
13044            editor.insert_creases(Some(crease), cx);
13045            let snapshot = editor.snapshot(cx);
13046            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13047            snapshot
13048        })
13049        .unwrap();
13050
13051    let render_args = render_args.lock().take().unwrap();
13052    assert_eq!(render_args.row, MultiBufferRow(1));
13053    assert_eq!(render_args.folded, false);
13054    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13055
13056    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13057        .unwrap();
13058    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13059    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13060
13061    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13062        .unwrap();
13063    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13064    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13065}
13066
13067fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13068    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13069    point..point
13070}
13071
13072fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13073    let (text, ranges) = marked_text_ranges(marked_text, true);
13074    assert_eq!(view.text(cx), text);
13075    assert_eq!(
13076        view.selections.ranges(cx),
13077        ranges,
13078        "Assert selections are {}",
13079        marked_text
13080    );
13081}
13082
13083pub fn handle_signature_help_request(
13084    cx: &mut EditorLspTestContext,
13085    mocked_response: lsp::SignatureHelp,
13086) -> impl Future<Output = ()> {
13087    let mut request =
13088        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13089            let mocked_response = mocked_response.clone();
13090            async move { Ok(Some(mocked_response)) }
13091        });
13092
13093    async move {
13094        request.next().await;
13095    }
13096}
13097
13098/// Handle completion request passing a marked string specifying where the completion
13099/// should be triggered from using '|' character, what range should be replaced, and what completions
13100/// should be returned using '<' and '>' to delimit the range
13101pub fn handle_completion_request(
13102    cx: &mut EditorLspTestContext,
13103    marked_string: &str,
13104    completions: Vec<&'static str>,
13105    counter: Arc<AtomicUsize>,
13106) -> impl Future<Output = ()> {
13107    let complete_from_marker: TextRangeMarker = '|'.into();
13108    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13109    let (_, mut marked_ranges) = marked_text_ranges_by(
13110        marked_string,
13111        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13112    );
13113
13114    let complete_from_position =
13115        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13116    let replace_range =
13117        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13118
13119    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13120        let completions = completions.clone();
13121        counter.fetch_add(1, atomic::Ordering::Release);
13122        async move {
13123            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13124            assert_eq!(
13125                params.text_document_position.position,
13126                complete_from_position
13127            );
13128            Ok(Some(lsp::CompletionResponse::Array(
13129                completions
13130                    .iter()
13131                    .map(|completion_text| lsp::CompletionItem {
13132                        label: completion_text.to_string(),
13133                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13134                            range: replace_range,
13135                            new_text: completion_text.to_string(),
13136                        })),
13137                        ..Default::default()
13138                    })
13139                    .collect(),
13140            )))
13141        }
13142    });
13143
13144    async move {
13145        request.next().await;
13146    }
13147}
13148
13149fn handle_resolve_completion_request(
13150    cx: &mut EditorLspTestContext,
13151    edits: Option<Vec<(&'static str, &'static str)>>,
13152) -> impl Future<Output = ()> {
13153    let edits = edits.map(|edits| {
13154        edits
13155            .iter()
13156            .map(|(marked_string, new_text)| {
13157                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13158                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13159                lsp::TextEdit::new(replace_range, new_text.to_string())
13160            })
13161            .collect::<Vec<_>>()
13162    });
13163
13164    let mut request =
13165        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13166            let edits = edits.clone();
13167            async move {
13168                Ok(lsp::CompletionItem {
13169                    additional_text_edits: edits,
13170                    ..Default::default()
13171                })
13172            }
13173        });
13174
13175    async move {
13176        request.next().await;
13177    }
13178}
13179
13180pub(crate) fn update_test_language_settings(
13181    cx: &mut TestAppContext,
13182    f: impl Fn(&mut AllLanguageSettingsContent),
13183) {
13184    _ = cx.update(|cx| {
13185        SettingsStore::update_global(cx, |store, cx| {
13186            store.update_user_settings::<AllLanguageSettings>(cx, f);
13187        });
13188    });
13189}
13190
13191pub(crate) fn update_test_project_settings(
13192    cx: &mut TestAppContext,
13193    f: impl Fn(&mut ProjectSettings),
13194) {
13195    _ = cx.update(|cx| {
13196        SettingsStore::update_global(cx, |store, cx| {
13197            store.update_user_settings::<ProjectSettings>(cx, f);
13198        });
13199    });
13200}
13201
13202pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13203    _ = cx.update(|cx| {
13204        assets::Assets.load_test_fonts(cx);
13205        let store = SettingsStore::test(cx);
13206        cx.set_global(store);
13207        theme::init(theme::LoadThemes::JustBase, cx);
13208        release_channel::init(SemanticVersion::default(), cx);
13209        client::init_settings(cx);
13210        language::init(cx);
13211        Project::init_settings(cx);
13212        workspace::init_settings(cx);
13213        crate::init(cx);
13214    });
13215
13216    update_test_language_settings(cx, f);
13217}
13218
13219pub(crate) fn rust_lang() -> Arc<Language> {
13220    Arc::new(Language::new(
13221        LanguageConfig {
13222            name: "Rust".into(),
13223            matcher: LanguageMatcher {
13224                path_suffixes: vec!["rs".to_string()],
13225                ..Default::default()
13226            },
13227            ..Default::default()
13228        },
13229        Some(tree_sitter_rust::language()),
13230    ))
13231}
13232
13233#[track_caller]
13234fn assert_hunk_revert(
13235    not_reverted_text_with_selections: &str,
13236    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13237    expected_reverted_text_with_selections: &str,
13238    base_text: &str,
13239    cx: &mut EditorLspTestContext,
13240) {
13241    cx.set_state(not_reverted_text_with_selections);
13242    cx.update_editor(|editor, cx| {
13243        editor
13244            .buffer()
13245            .read(cx)
13246            .as_singleton()
13247            .unwrap()
13248            .update(cx, |buffer, cx| {
13249                buffer.set_diff_base(Some(base_text.into()), cx);
13250            });
13251    });
13252    cx.executor().run_until_parked();
13253
13254    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13255        let snapshot = editor.buffer().read(cx).snapshot(cx);
13256        let reverted_hunk_statuses = snapshot
13257            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13258            .map(|hunk| hunk_status(&hunk))
13259            .collect::<Vec<_>>();
13260
13261        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13262        reverted_hunk_statuses
13263    });
13264    cx.executor().run_until_parked();
13265    cx.assert_editor_state(expected_reverted_text_with_selections);
13266    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13267}