editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::IndentGuideSettings;
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788            }],
 3789            Some(Autoscroll::fit()),
 3790            cx,
 3791        );
 3792        editor.change_selections(None, cx, |s| {
 3793            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3794        });
 3795        editor.move_line_down(&MoveLineDown, cx);
 3796    });
 3797}
 3798
 3799#[gpui::test]
 3800fn test_transpose(cx: &mut TestAppContext) {
 3801    init_test(cx, |_| {});
 3802
 3803    _ = cx.add_window(|cx| {
 3804        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3805        editor.set_style(EditorStyle::default(), cx);
 3806        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3807        editor.transpose(&Default::default(), cx);
 3808        assert_eq!(editor.text(cx), "bac");
 3809        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3810
 3811        editor.transpose(&Default::default(), cx);
 3812        assert_eq!(editor.text(cx), "bca");
 3813        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3814
 3815        editor.transpose(&Default::default(), cx);
 3816        assert_eq!(editor.text(cx), "bac");
 3817        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3818
 3819        editor
 3820    });
 3821
 3822    _ = cx.add_window(|cx| {
 3823        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3824        editor.set_style(EditorStyle::default(), cx);
 3825        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3826        editor.transpose(&Default::default(), cx);
 3827        assert_eq!(editor.text(cx), "acb\nde");
 3828        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3829
 3830        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3831        editor.transpose(&Default::default(), cx);
 3832        assert_eq!(editor.text(cx), "acbd\ne");
 3833        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3834
 3835        editor.transpose(&Default::default(), cx);
 3836        assert_eq!(editor.text(cx), "acbde\n");
 3837        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3838
 3839        editor.transpose(&Default::default(), cx);
 3840        assert_eq!(editor.text(cx), "acbd\ne");
 3841        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3842
 3843        editor
 3844    });
 3845
 3846    _ = cx.add_window(|cx| {
 3847        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3848        editor.set_style(EditorStyle::default(), cx);
 3849        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3850        editor.transpose(&Default::default(), cx);
 3851        assert_eq!(editor.text(cx), "bacd\ne");
 3852        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3853
 3854        editor.transpose(&Default::default(), cx);
 3855        assert_eq!(editor.text(cx), "bcade\n");
 3856        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3857
 3858        editor.transpose(&Default::default(), cx);
 3859        assert_eq!(editor.text(cx), "bcda\ne");
 3860        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3861
 3862        editor.transpose(&Default::default(), cx);
 3863        assert_eq!(editor.text(cx), "bcade\n");
 3864        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3865
 3866        editor.transpose(&Default::default(), cx);
 3867        assert_eq!(editor.text(cx), "bcaed\n");
 3868        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3869
 3870        editor
 3871    });
 3872
 3873    _ = cx.add_window(|cx| {
 3874        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3875        editor.set_style(EditorStyle::default(), cx);
 3876        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3877        editor.transpose(&Default::default(), cx);
 3878        assert_eq!(editor.text(cx), "🏀🍐✋");
 3879        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3880
 3881        editor.transpose(&Default::default(), cx);
 3882        assert_eq!(editor.text(cx), "🏀✋🍐");
 3883        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3884
 3885        editor.transpose(&Default::default(), cx);
 3886        assert_eq!(editor.text(cx), "🏀🍐✋");
 3887        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3888
 3889        editor
 3890    });
 3891}
 3892
 3893#[gpui::test]
 3894async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3895    init_test(cx, |_| {});
 3896
 3897    let mut cx = EditorTestContext::new(cx).await;
 3898
 3899    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3900    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3901    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3902
 3903    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3904    cx.set_state("two ˇfour ˇsix ˇ");
 3905    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3906    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3907
 3908    // Paste again but with only two cursors. Since the number of cursors doesn't
 3909    // match the number of slices in the clipboard, the entire clipboard text
 3910    // is pasted at each cursor.
 3911    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3912    cx.update_editor(|e, cx| {
 3913        e.handle_input("( ", cx);
 3914        e.paste(&Paste, cx);
 3915        e.handle_input(") ", cx);
 3916    });
 3917    cx.assert_editor_state(
 3918        &([
 3919            "( one✅ ",
 3920            "three ",
 3921            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3922            "three ",
 3923            "five ) ˇ",
 3924        ]
 3925        .join("\n")),
 3926    );
 3927
 3928    // Cut with three selections, one of which is full-line.
 3929    cx.set_state(indoc! {"
 3930        1«2ˇ»3
 3931        4ˇ567
 3932        «8ˇ»9"});
 3933    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3934    cx.assert_editor_state(indoc! {"
 3935        1ˇ3
 3936        ˇ9"});
 3937
 3938    // Paste with three selections, noticing how the copied selection that was full-line
 3939    // gets inserted before the second cursor.
 3940    cx.set_state(indoc! {"
 3941        1ˇ3
 3942 3943        «oˇ»ne"});
 3944    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3945    cx.assert_editor_state(indoc! {"
 3946        12ˇ3
 3947        4567
 3948 3949        8ˇne"});
 3950
 3951    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3952    cx.set_state(indoc! {"
 3953        The quick brown
 3954        fox juˇmps over
 3955        the lazy dog"});
 3956    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3957    assert_eq!(
 3958        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3959        Some("fox jumps over\n".to_owned())
 3960    );
 3961
 3962    // Paste with three selections, noticing how the copied full-line selection is inserted
 3963    // before the empty selections but replaces the selection that is non-empty.
 3964    cx.set_state(indoc! {"
 3965        Tˇhe quick brown
 3966        «foˇ»x jumps over
 3967        tˇhe lazy dog"});
 3968    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3969    cx.assert_editor_state(indoc! {"
 3970        fox jumps over
 3971        Tˇhe quick brown
 3972        fox jumps over
 3973        ˇx jumps over
 3974        fox jumps over
 3975        tˇhe lazy dog"});
 3976}
 3977
 3978#[gpui::test]
 3979async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3980    init_test(cx, |_| {});
 3981
 3982    let mut cx = EditorTestContext::new(cx).await;
 3983    let language = Arc::new(Language::new(
 3984        LanguageConfig::default(),
 3985        Some(tree_sitter_rust::language()),
 3986    ));
 3987    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3988
 3989    // Cut an indented block, without the leading whitespace.
 3990    cx.set_state(indoc! {"
 3991        const a: B = (
 3992            c(),
 3993            «d(
 3994                e,
 3995                f
 3996            )ˇ»
 3997        );
 3998    "});
 3999    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4000    cx.assert_editor_state(indoc! {"
 4001        const a: B = (
 4002            c(),
 4003            ˇ
 4004        );
 4005    "});
 4006
 4007    // Paste it at the same position.
 4008    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4009    cx.assert_editor_state(indoc! {"
 4010        const a: B = (
 4011            c(),
 4012            d(
 4013                e,
 4014                f
 4015 4016        );
 4017    "});
 4018
 4019    // Paste it at a line with a lower indent level.
 4020    cx.set_state(indoc! {"
 4021        ˇ
 4022        const a: B = (
 4023            c(),
 4024        );
 4025    "});
 4026    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4027    cx.assert_editor_state(indoc! {"
 4028        d(
 4029            e,
 4030            f
 4031 4032        const a: B = (
 4033            c(),
 4034        );
 4035    "});
 4036
 4037    // Cut an indented block, with the leading whitespace.
 4038    cx.set_state(indoc! {"
 4039        const a: B = (
 4040            c(),
 4041        «    d(
 4042                e,
 4043                f
 4044            )
 4045        ˇ»);
 4046    "});
 4047    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4048    cx.assert_editor_state(indoc! {"
 4049        const a: B = (
 4050            c(),
 4051        ˇ);
 4052    "});
 4053
 4054    // Paste it at the same position.
 4055    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4056    cx.assert_editor_state(indoc! {"
 4057        const a: B = (
 4058            c(),
 4059            d(
 4060                e,
 4061                f
 4062            )
 4063        ˇ);
 4064    "});
 4065
 4066    // Paste it at a line with a higher indent level.
 4067    cx.set_state(indoc! {"
 4068        const a: B = (
 4069            c(),
 4070            d(
 4071                e,
 4072 4073            )
 4074        );
 4075    "});
 4076    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4077    cx.assert_editor_state(indoc! {"
 4078        const a: B = (
 4079            c(),
 4080            d(
 4081                e,
 4082                f    d(
 4083                    e,
 4084                    f
 4085                )
 4086        ˇ
 4087            )
 4088        );
 4089    "});
 4090}
 4091
 4092#[gpui::test]
 4093fn test_select_all(cx: &mut TestAppContext) {
 4094    init_test(cx, |_| {});
 4095
 4096    let view = cx.add_window(|cx| {
 4097        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4098        build_editor(buffer, cx)
 4099    });
 4100    _ = view.update(cx, |view, cx| {
 4101        view.select_all(&SelectAll, cx);
 4102        assert_eq!(
 4103            view.selections.display_ranges(cx),
 4104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4105        );
 4106    });
 4107}
 4108
 4109#[gpui::test]
 4110fn test_select_line(cx: &mut TestAppContext) {
 4111    init_test(cx, |_| {});
 4112
 4113    let view = cx.add_window(|cx| {
 4114        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4115        build_editor(buffer, cx)
 4116    });
 4117    _ = view.update(cx, |view, cx| {
 4118        view.change_selections(None, cx, |s| {
 4119            s.select_display_ranges([
 4120                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4121                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4122                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4123                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4124            ])
 4125        });
 4126        view.select_line(&SelectLine, cx);
 4127        assert_eq!(
 4128            view.selections.display_ranges(cx),
 4129            vec![
 4130                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    _ = view.update(cx, |view, cx| {
 4137        view.select_line(&SelectLine, cx);
 4138        assert_eq!(
 4139            view.selections.display_ranges(cx),
 4140            vec![
 4141                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4142                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4143            ]
 4144        );
 4145    });
 4146
 4147    _ = view.update(cx, |view, cx| {
 4148        view.select_line(&SelectLine, cx);
 4149        assert_eq!(
 4150            view.selections.display_ranges(cx),
 4151            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4152        );
 4153    });
 4154}
 4155
 4156#[gpui::test]
 4157fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4158    init_test(cx, |_| {});
 4159
 4160    let view = cx.add_window(|cx| {
 4161        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4162        build_editor(buffer, cx)
 4163    });
 4164    _ = view.update(cx, |view, cx| {
 4165        view.fold_ranges(
 4166            vec![
 4167                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4168                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4169                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4170            ],
 4171            true,
 4172            cx,
 4173        );
 4174        view.change_selections(None, cx, |s| {
 4175            s.select_display_ranges([
 4176                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4177                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4178                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4179                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4180            ])
 4181        });
 4182        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4183    });
 4184
 4185    _ = view.update(cx, |view, cx| {
 4186        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4187        assert_eq!(
 4188            view.display_text(cx),
 4189            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4190        );
 4191        assert_eq!(
 4192            view.selections.display_ranges(cx),
 4193            [
 4194                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4195                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4196                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4197                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4198            ]
 4199        );
 4200    });
 4201
 4202    _ = view.update(cx, |view, cx| {
 4203        view.change_selections(None, cx, |s| {
 4204            s.select_display_ranges([
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4206            ])
 4207        });
 4208        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4209        assert_eq!(
 4210            view.display_text(cx),
 4211            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4212        );
 4213        assert_eq!(
 4214            view.selections.display_ranges(cx),
 4215            [
 4216                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4217                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4218                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4219                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4220                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4221                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4222                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4223                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4224            ]
 4225        );
 4226    });
 4227}
 4228
 4229#[gpui::test]
 4230async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    let mut cx = EditorTestContext::new(cx).await;
 4234
 4235    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4236    cx.set_state(indoc!(
 4237        r#"abc
 4238           defˇghi
 4239
 4240           jk
 4241           nlmo
 4242           "#
 4243    ));
 4244
 4245    cx.update_editor(|editor, cx| {
 4246        editor.add_selection_above(&Default::default(), cx);
 4247    });
 4248
 4249    cx.assert_editor_state(indoc!(
 4250        r#"abcˇ
 4251           defˇghi
 4252
 4253           jk
 4254           nlmo
 4255           "#
 4256    ));
 4257
 4258    cx.update_editor(|editor, cx| {
 4259        editor.add_selection_above(&Default::default(), cx);
 4260    });
 4261
 4262    cx.assert_editor_state(indoc!(
 4263        r#"abcˇ
 4264            defˇghi
 4265
 4266            jk
 4267            nlmo
 4268            "#
 4269    ));
 4270
 4271    cx.update_editor(|view, cx| {
 4272        view.add_selection_below(&Default::default(), cx);
 4273    });
 4274
 4275    cx.assert_editor_state(indoc!(
 4276        r#"abc
 4277           defˇghi
 4278
 4279           jk
 4280           nlmo
 4281           "#
 4282    ));
 4283
 4284    cx.update_editor(|view, cx| {
 4285        view.undo_selection(&Default::default(), cx);
 4286    });
 4287
 4288    cx.assert_editor_state(indoc!(
 4289        r#"abcˇ
 4290           defˇghi
 4291
 4292           jk
 4293           nlmo
 4294           "#
 4295    ));
 4296
 4297    cx.update_editor(|view, cx| {
 4298        view.redo_selection(&Default::default(), cx);
 4299    });
 4300
 4301    cx.assert_editor_state(indoc!(
 4302        r#"abc
 4303           defˇghi
 4304
 4305           jk
 4306           nlmo
 4307           "#
 4308    ));
 4309
 4310    cx.update_editor(|view, cx| {
 4311        view.add_selection_below(&Default::default(), cx);
 4312    });
 4313
 4314    cx.assert_editor_state(indoc!(
 4315        r#"abc
 4316           defˇghi
 4317
 4318           jk
 4319           nlmˇo
 4320           "#
 4321    ));
 4322
 4323    cx.update_editor(|view, cx| {
 4324        view.add_selection_below(&Default::default(), cx);
 4325    });
 4326
 4327    cx.assert_editor_state(indoc!(
 4328        r#"abc
 4329           defˇghi
 4330
 4331           jk
 4332           nlmˇo
 4333           "#
 4334    ));
 4335
 4336    // change selections
 4337    cx.set_state(indoc!(
 4338        r#"abc
 4339           def«ˇg»hi
 4340
 4341           jk
 4342           nlmo
 4343           "#
 4344    ));
 4345
 4346    cx.update_editor(|view, cx| {
 4347        view.add_selection_below(&Default::default(), cx);
 4348    });
 4349
 4350    cx.assert_editor_state(indoc!(
 4351        r#"abc
 4352           def«ˇg»hi
 4353
 4354           jk
 4355           nlm«ˇo»
 4356           "#
 4357    ));
 4358
 4359    cx.update_editor(|view, cx| {
 4360        view.add_selection_below(&Default::default(), cx);
 4361    });
 4362
 4363    cx.assert_editor_state(indoc!(
 4364        r#"abc
 4365           def«ˇg»hi
 4366
 4367           jk
 4368           nlm«ˇo»
 4369           "#
 4370    ));
 4371
 4372    cx.update_editor(|view, cx| {
 4373        view.add_selection_above(&Default::default(), cx);
 4374    });
 4375
 4376    cx.assert_editor_state(indoc!(
 4377        r#"abc
 4378           def«ˇg»hi
 4379
 4380           jk
 4381           nlmo
 4382           "#
 4383    ));
 4384
 4385    cx.update_editor(|view, cx| {
 4386        view.add_selection_above(&Default::default(), cx);
 4387    });
 4388
 4389    cx.assert_editor_state(indoc!(
 4390        r#"abc
 4391           def«ˇg»hi
 4392
 4393           jk
 4394           nlmo
 4395           "#
 4396    ));
 4397
 4398    // Change selections again
 4399    cx.set_state(indoc!(
 4400        r#"a«bc
 4401           defgˇ»hi
 4402
 4403           jk
 4404           nlmo
 4405           "#
 4406    ));
 4407
 4408    cx.update_editor(|view, cx| {
 4409        view.add_selection_below(&Default::default(), cx);
 4410    });
 4411
 4412    cx.assert_editor_state(indoc!(
 4413        r#"a«bcˇ»
 4414           d«efgˇ»hi
 4415
 4416           j«kˇ»
 4417           nlmo
 4418           "#
 4419    ));
 4420
 4421    cx.update_editor(|view, cx| {
 4422        view.add_selection_below(&Default::default(), cx);
 4423    });
 4424    cx.assert_editor_state(indoc!(
 4425        r#"a«bcˇ»
 4426           d«efgˇ»hi
 4427
 4428           j«kˇ»
 4429           n«lmoˇ»
 4430           "#
 4431    ));
 4432    cx.update_editor(|view, cx| {
 4433        view.add_selection_above(&Default::default(), cx);
 4434    });
 4435
 4436    cx.assert_editor_state(indoc!(
 4437        r#"a«bcˇ»
 4438           d«efgˇ»hi
 4439
 4440           j«kˇ»
 4441           nlmo
 4442           "#
 4443    ));
 4444
 4445    // Change selections again
 4446    cx.set_state(indoc!(
 4447        r#"abc
 4448           d«ˇefghi
 4449
 4450           jk
 4451           nlm»o
 4452           "#
 4453    ));
 4454
 4455    cx.update_editor(|view, cx| {
 4456        view.add_selection_above(&Default::default(), cx);
 4457    });
 4458
 4459    cx.assert_editor_state(indoc!(
 4460        r#"a«ˇbc»
 4461           d«ˇef»ghi
 4462
 4463           j«ˇk»
 4464           n«ˇlm»o
 4465           "#
 4466    ));
 4467
 4468    cx.update_editor(|view, cx| {
 4469        view.add_selection_below(&Default::default(), cx);
 4470    });
 4471
 4472    cx.assert_editor_state(indoc!(
 4473        r#"abc
 4474           d«ˇef»ghi
 4475
 4476           j«ˇk»
 4477           n«ˇlm»o
 4478           "#
 4479    ));
 4480}
 4481
 4482#[gpui::test]
 4483async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4484    init_test(cx, |_| {});
 4485
 4486    let mut cx = EditorTestContext::new(cx).await;
 4487    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4488
 4489    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4490        .unwrap();
 4491    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4492
 4493    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4494        .unwrap();
 4495    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4496
 4497    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4498    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4499
 4500    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4501    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4502
 4503    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4504        .unwrap();
 4505    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4506
 4507    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4508        .unwrap();
 4509    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4510}
 4511
 4512#[gpui::test]
 4513async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4514    init_test(cx, |_| {});
 4515
 4516    let mut cx = EditorTestContext::new(cx).await;
 4517    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4518
 4519    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4520        .unwrap();
 4521    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4522}
 4523
 4524#[gpui::test]
 4525async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4526    init_test(cx, |_| {});
 4527
 4528    let mut cx = EditorTestContext::new(cx).await;
 4529    cx.set_state(
 4530        r#"let foo = 2;
 4531lˇet foo = 2;
 4532let fooˇ = 2;
 4533let foo = 2;
 4534let foo = ˇ2;"#,
 4535    );
 4536
 4537    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4538        .unwrap();
 4539    cx.assert_editor_state(
 4540        r#"let foo = 2;
 4541«letˇ» foo = 2;
 4542let «fooˇ» = 2;
 4543let foo = 2;
 4544let foo = «2ˇ»;"#,
 4545    );
 4546
 4547    // noop for multiple selections with different contents
 4548    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4549        .unwrap();
 4550    cx.assert_editor_state(
 4551        r#"let foo = 2;
 4552«letˇ» foo = 2;
 4553let «fooˇ» = 2;
 4554let foo = 2;
 4555let foo = «2ˇ»;"#,
 4556    );
 4557}
 4558
 4559#[gpui::test]
 4560async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4561    init_test(cx, |_| {});
 4562
 4563    let mut cx = EditorTestContext::new_multibuffer(
 4564        cx,
 4565        [
 4566            &indoc! {
 4567                "aaa\n«bbb\nccc\n»ddd"
 4568            },
 4569            &indoc! {
 4570                "aaa\n«bbb\nccc\n»ddd"
 4571            },
 4572        ],
 4573    );
 4574
 4575    cx.assert_editor_state(indoc! {"
 4576        ˇbbb
 4577        ccc
 4578
 4579        bbb
 4580        ccc
 4581        "});
 4582    cx.dispatch_action(SelectPrevious::default());
 4583    cx.assert_editor_state(indoc! {"
 4584                «bbbˇ»
 4585                ccc
 4586
 4587                bbb
 4588                ccc
 4589                "});
 4590    cx.dispatch_action(SelectPrevious::default());
 4591    cx.assert_editor_state(indoc! {"
 4592                «bbbˇ»
 4593                ccc
 4594
 4595                «bbbˇ»
 4596                ccc
 4597                "});
 4598}
 4599
 4600#[gpui::test]
 4601async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4602    init_test(cx, |_| {});
 4603
 4604    let mut cx = EditorTestContext::new(cx).await;
 4605    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4606
 4607    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4608        .unwrap();
 4609    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4610
 4611    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4612        .unwrap();
 4613    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4614
 4615    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4616    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4617
 4618    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4619    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4620
 4621    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4622        .unwrap();
 4623    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4624
 4625    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4626        .unwrap();
 4627    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4628
 4629    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4630        .unwrap();
 4631    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4632}
 4633
 4634#[gpui::test]
 4635async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4636    init_test(cx, |_| {});
 4637
 4638    let mut cx = EditorTestContext::new(cx).await;
 4639    cx.set_state(
 4640        r#"let foo = 2;
 4641lˇet foo = 2;
 4642let fooˇ = 2;
 4643let foo = 2;
 4644let foo = ˇ2;"#,
 4645    );
 4646
 4647    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4648        .unwrap();
 4649    cx.assert_editor_state(
 4650        r#"let foo = 2;
 4651«letˇ» foo = 2;
 4652let «fooˇ» = 2;
 4653let foo = 2;
 4654let foo = «2ˇ»;"#,
 4655    );
 4656
 4657    // noop for multiple selections with different contents
 4658    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4659        .unwrap();
 4660    cx.assert_editor_state(
 4661        r#"let foo = 2;
 4662«letˇ» foo = 2;
 4663let «fooˇ» = 2;
 4664let foo = 2;
 4665let foo = «2ˇ»;"#,
 4666    );
 4667}
 4668
 4669#[gpui::test]
 4670async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4671    init_test(cx, |_| {});
 4672
 4673    let mut cx = EditorTestContext::new(cx).await;
 4674    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4675
 4676    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4677        .unwrap();
 4678    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4679
 4680    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4681        .unwrap();
 4682    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4683
 4684    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4685    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4686
 4687    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4688    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4689
 4690    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4691        .unwrap();
 4692    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4693
 4694    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4695        .unwrap();
 4696    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4697}
 4698
 4699#[gpui::test]
 4700async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4701    init_test(cx, |_| {});
 4702
 4703    let language = Arc::new(Language::new(
 4704        LanguageConfig::default(),
 4705        Some(tree_sitter_rust::language()),
 4706    ));
 4707
 4708    let text = r#"
 4709        use mod1::mod2::{mod3, mod4};
 4710
 4711        fn fn_1(param1: bool, param2: &str) {
 4712            let var1 = "text";
 4713        }
 4714    "#
 4715    .unindent();
 4716
 4717    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4718    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4719    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4720
 4721    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4722        .await;
 4723
 4724    _ = view.update(cx, |view, cx| {
 4725        view.change_selections(None, cx, |s| {
 4726            s.select_display_ranges([
 4727                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4728                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4729                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4730            ]);
 4731        });
 4732        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4733    });
 4734    assert_eq!(
 4735        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
 4736        &[
 4737            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4738            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4739            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4740        ]
 4741    );
 4742
 4743    _ = view.update(cx, |view, cx| {
 4744        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4745    });
 4746    assert_eq!(
 4747        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4748        &[
 4749            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4750            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4751        ]
 4752    );
 4753
 4754    _ = view.update(cx, |view, cx| {
 4755        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4756    });
 4757    assert_eq!(
 4758        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4759        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4760    );
 4761
 4762    // Trying to expand the selected syntax node one more time has no effect.
 4763    _ = view.update(cx, |view, cx| {
 4764        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4765    });
 4766    assert_eq!(
 4767        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4768        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4769    );
 4770
 4771    _ = view.update(cx, |view, cx| {
 4772        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4773    });
 4774    assert_eq!(
 4775        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4776        &[
 4777            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4778            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4779        ]
 4780    );
 4781
 4782    _ = view.update(cx, |view, cx| {
 4783        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4784    });
 4785    assert_eq!(
 4786        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4787        &[
 4788            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4789            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4790            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4791        ]
 4792    );
 4793
 4794    _ = view.update(cx, |view, cx| {
 4795        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4796    });
 4797    assert_eq!(
 4798        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4799        &[
 4800            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4801            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4802            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4803        ]
 4804    );
 4805
 4806    // Trying to shrink the selected syntax node one more time has no effect.
 4807    _ = view.update(cx, |view, cx| {
 4808        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4809    });
 4810    assert_eq!(
 4811        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4812        &[
 4813            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4814            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4815            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4816        ]
 4817    );
 4818
 4819    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4820    // a fold.
 4821    _ = view.update(cx, |view, cx| {
 4822        view.fold_ranges(
 4823            vec![
 4824                (
 4825                    Point::new(0, 21)..Point::new(0, 24),
 4826                    FoldPlaceholder::test(),
 4827                ),
 4828                (
 4829                    Point::new(3, 20)..Point::new(3, 22),
 4830                    FoldPlaceholder::test(),
 4831                ),
 4832            ],
 4833            true,
 4834            cx,
 4835        );
 4836        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4837    });
 4838    assert_eq!(
 4839        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4840        &[
 4841            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4842            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4843            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4844        ]
 4845    );
 4846}
 4847
 4848#[gpui::test]
 4849async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4850    init_test(cx, |_| {});
 4851
 4852    let language = Arc::new(
 4853        Language::new(
 4854            LanguageConfig {
 4855                brackets: BracketPairConfig {
 4856                    pairs: vec![
 4857                        BracketPair {
 4858                            start: "{".to_string(),
 4859                            end: "}".to_string(),
 4860                            close: false,
 4861                            surround: false,
 4862                            newline: true,
 4863                        },
 4864                        BracketPair {
 4865                            start: "(".to_string(),
 4866                            end: ")".to_string(),
 4867                            close: false,
 4868                            surround: false,
 4869                            newline: true,
 4870                        },
 4871                    ],
 4872                    ..Default::default()
 4873                },
 4874                ..Default::default()
 4875            },
 4876            Some(tree_sitter_rust::language()),
 4877        )
 4878        .with_indents_query(
 4879            r#"
 4880                (_ "(" ")" @end) @indent
 4881                (_ "{" "}" @end) @indent
 4882            "#,
 4883        )
 4884        .unwrap(),
 4885    );
 4886
 4887    let text = "fn a() {}";
 4888
 4889    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4890    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4891    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4892    editor
 4893        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4894        .await;
 4895
 4896    _ = editor.update(cx, |editor, cx| {
 4897        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4898        editor.newline(&Newline, cx);
 4899        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4900        assert_eq!(
 4901            editor.selections.ranges(cx),
 4902            &[
 4903                Point::new(1, 4)..Point::new(1, 4),
 4904                Point::new(3, 4)..Point::new(3, 4),
 4905                Point::new(5, 0)..Point::new(5, 0)
 4906            ]
 4907        );
 4908    });
 4909}
 4910
 4911#[gpui::test]
 4912async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4913    init_test(cx, |_| {});
 4914
 4915    let mut cx = EditorTestContext::new(cx).await;
 4916
 4917    let language = Arc::new(Language::new(
 4918        LanguageConfig {
 4919            brackets: BracketPairConfig {
 4920                pairs: vec![
 4921                    BracketPair {
 4922                        start: "{".to_string(),
 4923                        end: "}".to_string(),
 4924                        close: true,
 4925                        surround: true,
 4926                        newline: true,
 4927                    },
 4928                    BracketPair {
 4929                        start: "(".to_string(),
 4930                        end: ")".to_string(),
 4931                        close: true,
 4932                        surround: true,
 4933                        newline: true,
 4934                    },
 4935                    BracketPair {
 4936                        start: "/*".to_string(),
 4937                        end: " */".to_string(),
 4938                        close: true,
 4939                        surround: true,
 4940                        newline: true,
 4941                    },
 4942                    BracketPair {
 4943                        start: "[".to_string(),
 4944                        end: "]".to_string(),
 4945                        close: false,
 4946                        surround: false,
 4947                        newline: true,
 4948                    },
 4949                    BracketPair {
 4950                        start: "\"".to_string(),
 4951                        end: "\"".to_string(),
 4952                        close: true,
 4953                        surround: true,
 4954                        newline: false,
 4955                    },
 4956                    BracketPair {
 4957                        start: "<".to_string(),
 4958                        end: ">".to_string(),
 4959                        close: false,
 4960                        surround: true,
 4961                        newline: true,
 4962                    },
 4963                ],
 4964                ..Default::default()
 4965            },
 4966            autoclose_before: "})]".to_string(),
 4967            ..Default::default()
 4968        },
 4969        Some(tree_sitter_rust::language()),
 4970    ));
 4971
 4972    cx.language_registry().add(language.clone());
 4973    cx.update_buffer(|buffer, cx| {
 4974        buffer.set_language(Some(language), cx);
 4975    });
 4976
 4977    cx.set_state(
 4978        &r#"
 4979            🏀ˇ
 4980            εˇ
 4981            ❤️ˇ
 4982        "#
 4983        .unindent(),
 4984    );
 4985
 4986    // autoclose multiple nested brackets at multiple cursors
 4987    cx.update_editor(|view, cx| {
 4988        view.handle_input("{", cx);
 4989        view.handle_input("{", cx);
 4990        view.handle_input("{", cx);
 4991    });
 4992    cx.assert_editor_state(
 4993        &"
 4994            🏀{{{ˇ}}}
 4995            ε{{{ˇ}}}
 4996            ❤️{{{ˇ}}}
 4997        "
 4998        .unindent(),
 4999    );
 5000
 5001    // insert a different closing bracket
 5002    cx.update_editor(|view, cx| {
 5003        view.handle_input(")", cx);
 5004    });
 5005    cx.assert_editor_state(
 5006        &"
 5007            🏀{{{)ˇ}}}
 5008            ε{{{)ˇ}}}
 5009            ❤️{{{)ˇ}}}
 5010        "
 5011        .unindent(),
 5012    );
 5013
 5014    // skip over the auto-closed brackets when typing a closing bracket
 5015    cx.update_editor(|view, cx| {
 5016        view.move_right(&MoveRight, cx);
 5017        view.handle_input("}", cx);
 5018        view.handle_input("}", cx);
 5019        view.handle_input("}", cx);
 5020    });
 5021    cx.assert_editor_state(
 5022        &"
 5023            🏀{{{)}}}}ˇ
 5024            ε{{{)}}}}ˇ
 5025            ❤️{{{)}}}}ˇ
 5026        "
 5027        .unindent(),
 5028    );
 5029
 5030    // autoclose multi-character pairs
 5031    cx.set_state(
 5032        &"
 5033            ˇ
 5034            ˇ
 5035        "
 5036        .unindent(),
 5037    );
 5038    cx.update_editor(|view, cx| {
 5039        view.handle_input("/", cx);
 5040        view.handle_input("*", cx);
 5041    });
 5042    cx.assert_editor_state(
 5043        &"
 5044            /*ˇ */
 5045            /*ˇ */
 5046        "
 5047        .unindent(),
 5048    );
 5049
 5050    // one cursor autocloses a multi-character pair, one cursor
 5051    // does not autoclose.
 5052    cx.set_state(
 5053        &"
 5054 5055            ˇ
 5056        "
 5057        .unindent(),
 5058    );
 5059    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5060    cx.assert_editor_state(
 5061        &"
 5062            /*ˇ */
 5063 5064        "
 5065        .unindent(),
 5066    );
 5067
 5068    // Don't autoclose if the next character isn't whitespace and isn't
 5069    // listed in the language's "autoclose_before" section.
 5070    cx.set_state("ˇa b");
 5071    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5072    cx.assert_editor_state("{ˇa b");
 5073
 5074    // Don't autoclose if `close` is false for the bracket pair
 5075    cx.set_state("ˇ");
 5076    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5077    cx.assert_editor_state("");
 5078
 5079    // Surround with brackets if text is selected
 5080    cx.set_state("«aˇ» b");
 5081    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5082    cx.assert_editor_state("{«aˇ»} b");
 5083
 5084    // Autclose pair where the start and end characters are the same
 5085    cx.set_state("");
 5086    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5087    cx.assert_editor_state("a\"ˇ\"");
 5088    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5089    cx.assert_editor_state("a\"\"ˇ");
 5090
 5091    // Don't autoclose pair if autoclose is disabled
 5092    cx.set_state("ˇ");
 5093    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5094    cx.assert_editor_state("");
 5095
 5096    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5097    cx.set_state("«aˇ» b");
 5098    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5099    cx.assert_editor_state("<«aˇ»> b");
 5100}
 5101
 5102#[gpui::test]
 5103async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5104    init_test(cx, |settings| {
 5105        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5106    });
 5107
 5108    let mut cx = EditorTestContext::new(cx).await;
 5109
 5110    let language = Arc::new(Language::new(
 5111        LanguageConfig {
 5112            brackets: BracketPairConfig {
 5113                pairs: vec![
 5114                    BracketPair {
 5115                        start: "{".to_string(),
 5116                        end: "}".to_string(),
 5117                        close: true,
 5118                        surround: true,
 5119                        newline: true,
 5120                    },
 5121                    BracketPair {
 5122                        start: "(".to_string(),
 5123                        end: ")".to_string(),
 5124                        close: true,
 5125                        surround: true,
 5126                        newline: true,
 5127                    },
 5128                    BracketPair {
 5129                        start: "[".to_string(),
 5130                        end: "]".to_string(),
 5131                        close: false,
 5132                        surround: false,
 5133                        newline: true,
 5134                    },
 5135                ],
 5136                ..Default::default()
 5137            },
 5138            autoclose_before: "})]".to_string(),
 5139            ..Default::default()
 5140        },
 5141        Some(tree_sitter_rust::language()),
 5142    ));
 5143
 5144    cx.language_registry().add(language.clone());
 5145    cx.update_buffer(|buffer, cx| {
 5146        buffer.set_language(Some(language), cx);
 5147    });
 5148
 5149    cx.set_state(
 5150        &"
 5151            ˇ
 5152            ˇ
 5153            ˇ
 5154        "
 5155        .unindent(),
 5156    );
 5157
 5158    // ensure only matching closing brackets are skipped over
 5159    cx.update_editor(|view, cx| {
 5160        view.handle_input("}", cx);
 5161        view.move_left(&MoveLeft, cx);
 5162        view.handle_input(")", cx);
 5163        view.move_left(&MoveLeft, cx);
 5164    });
 5165    cx.assert_editor_state(
 5166        &"
 5167            ˇ)}
 5168            ˇ)}
 5169            ˇ)}
 5170        "
 5171        .unindent(),
 5172    );
 5173
 5174    // skip-over closing brackets at multiple cursors
 5175    cx.update_editor(|view, cx| {
 5176        view.handle_input(")", cx);
 5177        view.handle_input("}", cx);
 5178    });
 5179    cx.assert_editor_state(
 5180        &"
 5181            )}ˇ
 5182            )}ˇ
 5183            )}ˇ
 5184        "
 5185        .unindent(),
 5186    );
 5187
 5188    // ignore non-close brackets
 5189    cx.update_editor(|view, cx| {
 5190        view.handle_input("]", cx);
 5191        view.move_left(&MoveLeft, cx);
 5192        view.handle_input("]", cx);
 5193    });
 5194    cx.assert_editor_state(
 5195        &"
 5196            )}]ˇ]
 5197            )}]ˇ]
 5198            )}]ˇ]
 5199        "
 5200        .unindent(),
 5201    );
 5202}
 5203
 5204#[gpui::test]
 5205async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5206    init_test(cx, |_| {});
 5207
 5208    let mut cx = EditorTestContext::new(cx).await;
 5209
 5210    let html_language = Arc::new(
 5211        Language::new(
 5212            LanguageConfig {
 5213                name: "HTML".into(),
 5214                brackets: BracketPairConfig {
 5215                    pairs: vec![
 5216                        BracketPair {
 5217                            start: "<".into(),
 5218                            end: ">".into(),
 5219                            close: true,
 5220                            ..Default::default()
 5221                        },
 5222                        BracketPair {
 5223                            start: "{".into(),
 5224                            end: "}".into(),
 5225                            close: true,
 5226                            ..Default::default()
 5227                        },
 5228                        BracketPair {
 5229                            start: "(".into(),
 5230                            end: ")".into(),
 5231                            close: true,
 5232                            ..Default::default()
 5233                        },
 5234                    ],
 5235                    ..Default::default()
 5236                },
 5237                autoclose_before: "})]>".into(),
 5238                ..Default::default()
 5239            },
 5240            Some(tree_sitter_html::language()),
 5241        )
 5242        .with_injection_query(
 5243            r#"
 5244            (script_element
 5245                (raw_text) @content
 5246                (#set! "language" "javascript"))
 5247            "#,
 5248        )
 5249        .unwrap(),
 5250    );
 5251
 5252    let javascript_language = Arc::new(Language::new(
 5253        LanguageConfig {
 5254            name: "JavaScript".into(),
 5255            brackets: BracketPairConfig {
 5256                pairs: vec![
 5257                    BracketPair {
 5258                        start: "/*".into(),
 5259                        end: " */".into(),
 5260                        close: true,
 5261                        ..Default::default()
 5262                    },
 5263                    BracketPair {
 5264                        start: "{".into(),
 5265                        end: "}".into(),
 5266                        close: true,
 5267                        ..Default::default()
 5268                    },
 5269                    BracketPair {
 5270                        start: "(".into(),
 5271                        end: ")".into(),
 5272                        close: true,
 5273                        ..Default::default()
 5274                    },
 5275                ],
 5276                ..Default::default()
 5277            },
 5278            autoclose_before: "})]>".into(),
 5279            ..Default::default()
 5280        },
 5281        Some(tree_sitter_typescript::language_tsx()),
 5282    ));
 5283
 5284    cx.language_registry().add(html_language.clone());
 5285    cx.language_registry().add(javascript_language.clone());
 5286
 5287    cx.update_buffer(|buffer, cx| {
 5288        buffer.set_language(Some(html_language), cx);
 5289    });
 5290
 5291    cx.set_state(
 5292        &r#"
 5293            <body>ˇ
 5294                <script>
 5295                    var x = 1;ˇ
 5296                </script>
 5297            </body>ˇ
 5298        "#
 5299        .unindent(),
 5300    );
 5301
 5302    // Precondition: different languages are active at different locations.
 5303    cx.update_editor(|editor, cx| {
 5304        let snapshot = editor.snapshot(cx);
 5305        let cursors = editor.selections.ranges::<usize>(cx);
 5306        let languages = cursors
 5307            .iter()
 5308            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5309            .collect::<Vec<_>>();
 5310        assert_eq!(
 5311            languages,
 5312            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5313        );
 5314    });
 5315
 5316    // Angle brackets autoclose in HTML, but not JavaScript.
 5317    cx.update_editor(|editor, cx| {
 5318        editor.handle_input("<", cx);
 5319        editor.handle_input("a", cx);
 5320    });
 5321    cx.assert_editor_state(
 5322        &r#"
 5323            <body><aˇ>
 5324                <script>
 5325                    var x = 1;<aˇ
 5326                </script>
 5327            </body><aˇ>
 5328        "#
 5329        .unindent(),
 5330    );
 5331
 5332    // Curly braces and parens autoclose in both HTML and JavaScript.
 5333    cx.update_editor(|editor, cx| {
 5334        editor.handle_input(" b=", cx);
 5335        editor.handle_input("{", cx);
 5336        editor.handle_input("c", cx);
 5337        editor.handle_input("(", cx);
 5338    });
 5339    cx.assert_editor_state(
 5340        &r#"
 5341            <body><a b={c(ˇ)}>
 5342                <script>
 5343                    var x = 1;<a b={c(ˇ)}
 5344                </script>
 5345            </body><a b={c(ˇ)}>
 5346        "#
 5347        .unindent(),
 5348    );
 5349
 5350    // Brackets that were already autoclosed are skipped.
 5351    cx.update_editor(|editor, cx| {
 5352        editor.handle_input(")", cx);
 5353        editor.handle_input("d", cx);
 5354        editor.handle_input("}", cx);
 5355    });
 5356    cx.assert_editor_state(
 5357        &r#"
 5358            <body><a b={c()d}ˇ>
 5359                <script>
 5360                    var x = 1;<a b={c()d}ˇ
 5361                </script>
 5362            </body><a b={c()d}ˇ>
 5363        "#
 5364        .unindent(),
 5365    );
 5366    cx.update_editor(|editor, cx| {
 5367        editor.handle_input(">", cx);
 5368    });
 5369    cx.assert_editor_state(
 5370        &r#"
 5371            <body><a b={c()d}>ˇ
 5372                <script>
 5373                    var x = 1;<a b={c()d}>ˇ
 5374                </script>
 5375            </body><a b={c()d}>ˇ
 5376        "#
 5377        .unindent(),
 5378    );
 5379
 5380    // Reset
 5381    cx.set_state(
 5382        &r#"
 5383            <body>ˇ
 5384                <script>
 5385                    var x = 1;ˇ
 5386                </script>
 5387            </body>ˇ
 5388        "#
 5389        .unindent(),
 5390    );
 5391
 5392    cx.update_editor(|editor, cx| {
 5393        editor.handle_input("<", cx);
 5394    });
 5395    cx.assert_editor_state(
 5396        &r#"
 5397            <body><ˇ>
 5398                <script>
 5399                    var x = 1;<ˇ
 5400                </script>
 5401            </body><ˇ>
 5402        "#
 5403        .unindent(),
 5404    );
 5405
 5406    // When backspacing, the closing angle brackets are removed.
 5407    cx.update_editor(|editor, cx| {
 5408        editor.backspace(&Backspace, cx);
 5409    });
 5410    cx.assert_editor_state(
 5411        &r#"
 5412            <body>ˇ
 5413                <script>
 5414                    var x = 1;ˇ
 5415                </script>
 5416            </body>ˇ
 5417        "#
 5418        .unindent(),
 5419    );
 5420
 5421    // Block comments autoclose in JavaScript, but not HTML.
 5422    cx.update_editor(|editor, cx| {
 5423        editor.handle_input("/", cx);
 5424        editor.handle_input("*", cx);
 5425    });
 5426    cx.assert_editor_state(
 5427        &r#"
 5428            <body>/*ˇ
 5429                <script>
 5430                    var x = 1;/*ˇ */
 5431                </script>
 5432            </body>/*ˇ
 5433        "#
 5434        .unindent(),
 5435    );
 5436}
 5437
 5438#[gpui::test]
 5439async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5440    init_test(cx, |_| {});
 5441
 5442    let mut cx = EditorTestContext::new(cx).await;
 5443
 5444    let rust_language = Arc::new(
 5445        Language::new(
 5446            LanguageConfig {
 5447                name: "Rust".into(),
 5448                brackets: serde_json::from_value(json!([
 5449                    { "start": "{", "end": "}", "close": true, "newline": true },
 5450                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5451                ]))
 5452                .unwrap(),
 5453                autoclose_before: "})]>".into(),
 5454                ..Default::default()
 5455            },
 5456            Some(tree_sitter_rust::language()),
 5457        )
 5458        .with_override_query("(string_literal) @string")
 5459        .unwrap(),
 5460    );
 5461
 5462    cx.language_registry().add(rust_language.clone());
 5463    cx.update_buffer(|buffer, cx| {
 5464        buffer.set_language(Some(rust_language), cx);
 5465    });
 5466
 5467    cx.set_state(
 5468        &r#"
 5469            let x = ˇ
 5470        "#
 5471        .unindent(),
 5472    );
 5473
 5474    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5475    cx.update_editor(|editor, cx| {
 5476        editor.handle_input("\"", cx);
 5477    });
 5478    cx.assert_editor_state(
 5479        &r#"
 5480            let x = "ˇ"
 5481        "#
 5482        .unindent(),
 5483    );
 5484
 5485    // Inserting another quotation mark. The cursor moves across the existing
 5486    // automatically-inserted quotation mark.
 5487    cx.update_editor(|editor, cx| {
 5488        editor.handle_input("\"", cx);
 5489    });
 5490    cx.assert_editor_state(
 5491        &r#"
 5492            let x = ""ˇ
 5493        "#
 5494        .unindent(),
 5495    );
 5496
 5497    // Reset
 5498    cx.set_state(
 5499        &r#"
 5500            let x = ˇ
 5501        "#
 5502        .unindent(),
 5503    );
 5504
 5505    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5506    cx.update_editor(|editor, cx| {
 5507        editor.handle_input("\"", cx);
 5508        editor.handle_input(" ", cx);
 5509        editor.move_left(&Default::default(), cx);
 5510        editor.handle_input("\\", cx);
 5511        editor.handle_input("\"", cx);
 5512    });
 5513    cx.assert_editor_state(
 5514        &r#"
 5515            let x = "\"ˇ "
 5516        "#
 5517        .unindent(),
 5518    );
 5519
 5520    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5521    // mark. Nothing is inserted.
 5522    cx.update_editor(|editor, cx| {
 5523        editor.move_right(&Default::default(), cx);
 5524        editor.handle_input("\"", cx);
 5525    });
 5526    cx.assert_editor_state(
 5527        &r#"
 5528            let x = "\" "ˇ
 5529        "#
 5530        .unindent(),
 5531    );
 5532}
 5533
 5534#[gpui::test]
 5535async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5536    init_test(cx, |_| {});
 5537
 5538    let language = Arc::new(Language::new(
 5539        LanguageConfig {
 5540            brackets: BracketPairConfig {
 5541                pairs: vec![
 5542                    BracketPair {
 5543                        start: "{".to_string(),
 5544                        end: "}".to_string(),
 5545                        close: true,
 5546                        surround: true,
 5547                        newline: true,
 5548                    },
 5549                    BracketPair {
 5550                        start: "/* ".to_string(),
 5551                        end: "*/".to_string(),
 5552                        close: true,
 5553                        surround: true,
 5554                        ..Default::default()
 5555                    },
 5556                ],
 5557                ..Default::default()
 5558            },
 5559            ..Default::default()
 5560        },
 5561        Some(tree_sitter_rust::language()),
 5562    ));
 5563
 5564    let text = r#"
 5565        a
 5566        b
 5567        c
 5568    "#
 5569    .unindent();
 5570
 5571    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5572    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5573    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5574    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5575        .await;
 5576
 5577    _ = view.update(cx, |view, cx| {
 5578        view.change_selections(None, cx, |s| {
 5579            s.select_display_ranges([
 5580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5581                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5582                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5583            ])
 5584        });
 5585
 5586        view.handle_input("{", cx);
 5587        view.handle_input("{", cx);
 5588        view.handle_input("{", cx);
 5589        assert_eq!(
 5590            view.text(cx),
 5591            "
 5592                {{{a}}}
 5593                {{{b}}}
 5594                {{{c}}}
 5595            "
 5596            .unindent()
 5597        );
 5598        assert_eq!(
 5599            view.selections.display_ranges(cx),
 5600            [
 5601                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5602                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5603                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5604            ]
 5605        );
 5606
 5607        view.undo(&Undo, cx);
 5608        view.undo(&Undo, cx);
 5609        view.undo(&Undo, cx);
 5610        assert_eq!(
 5611            view.text(cx),
 5612            "
 5613                a
 5614                b
 5615                c
 5616            "
 5617            .unindent()
 5618        );
 5619        assert_eq!(
 5620            view.selections.display_ranges(cx),
 5621            [
 5622                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5623                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5624                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5625            ]
 5626        );
 5627
 5628        // Ensure inserting the first character of a multi-byte bracket pair
 5629        // doesn't surround the selections with the bracket.
 5630        view.handle_input("/", cx);
 5631        assert_eq!(
 5632            view.text(cx),
 5633            "
 5634                /
 5635                /
 5636                /
 5637            "
 5638            .unindent()
 5639        );
 5640        assert_eq!(
 5641            view.selections.display_ranges(cx),
 5642            [
 5643                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5644                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5645                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5646            ]
 5647        );
 5648
 5649        view.undo(&Undo, cx);
 5650        assert_eq!(
 5651            view.text(cx),
 5652            "
 5653                a
 5654                b
 5655                c
 5656            "
 5657            .unindent()
 5658        );
 5659        assert_eq!(
 5660            view.selections.display_ranges(cx),
 5661            [
 5662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5663                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5664                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5665            ]
 5666        );
 5667
 5668        // Ensure inserting the last character of a multi-byte bracket pair
 5669        // doesn't surround the selections with the bracket.
 5670        view.handle_input("*", cx);
 5671        assert_eq!(
 5672            view.text(cx),
 5673            "
 5674                *
 5675                *
 5676                *
 5677            "
 5678            .unindent()
 5679        );
 5680        assert_eq!(
 5681            view.selections.display_ranges(cx),
 5682            [
 5683                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5686            ]
 5687        );
 5688    });
 5689}
 5690
 5691#[gpui::test]
 5692async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5693    init_test(cx, |_| {});
 5694
 5695    let language = Arc::new(Language::new(
 5696        LanguageConfig {
 5697            brackets: BracketPairConfig {
 5698                pairs: vec![BracketPair {
 5699                    start: "{".to_string(),
 5700                    end: "}".to_string(),
 5701                    close: true,
 5702                    surround: true,
 5703                    newline: true,
 5704                }],
 5705                ..Default::default()
 5706            },
 5707            autoclose_before: "}".to_string(),
 5708            ..Default::default()
 5709        },
 5710        Some(tree_sitter_rust::language()),
 5711    ));
 5712
 5713    let text = r#"
 5714        a
 5715        b
 5716        c
 5717    "#
 5718    .unindent();
 5719
 5720    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5721    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5722    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5723    editor
 5724        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5725        .await;
 5726
 5727    _ = editor.update(cx, |editor, cx| {
 5728        editor.change_selections(None, cx, |s| {
 5729            s.select_ranges([
 5730                Point::new(0, 1)..Point::new(0, 1),
 5731                Point::new(1, 1)..Point::new(1, 1),
 5732                Point::new(2, 1)..Point::new(2, 1),
 5733            ])
 5734        });
 5735
 5736        editor.handle_input("{", cx);
 5737        editor.handle_input("{", cx);
 5738        editor.handle_input("_", cx);
 5739        assert_eq!(
 5740            editor.text(cx),
 5741            "
 5742                a{{_}}
 5743                b{{_}}
 5744                c{{_}}
 5745            "
 5746            .unindent()
 5747        );
 5748        assert_eq!(
 5749            editor.selections.ranges::<Point>(cx),
 5750            [
 5751                Point::new(0, 4)..Point::new(0, 4),
 5752                Point::new(1, 4)..Point::new(1, 4),
 5753                Point::new(2, 4)..Point::new(2, 4)
 5754            ]
 5755        );
 5756
 5757        editor.backspace(&Default::default(), cx);
 5758        editor.backspace(&Default::default(), cx);
 5759        assert_eq!(
 5760            editor.text(cx),
 5761            "
 5762                a{}
 5763                b{}
 5764                c{}
 5765            "
 5766            .unindent()
 5767        );
 5768        assert_eq!(
 5769            editor.selections.ranges::<Point>(cx),
 5770            [
 5771                Point::new(0, 2)..Point::new(0, 2),
 5772                Point::new(1, 2)..Point::new(1, 2),
 5773                Point::new(2, 2)..Point::new(2, 2)
 5774            ]
 5775        );
 5776
 5777        editor.delete_to_previous_word_start(&Default::default(), cx);
 5778        assert_eq!(
 5779            editor.text(cx),
 5780            "
 5781                a
 5782                b
 5783                c
 5784            "
 5785            .unindent()
 5786        );
 5787        assert_eq!(
 5788            editor.selections.ranges::<Point>(cx),
 5789            [
 5790                Point::new(0, 1)..Point::new(0, 1),
 5791                Point::new(1, 1)..Point::new(1, 1),
 5792                Point::new(2, 1)..Point::new(2, 1)
 5793            ]
 5794        );
 5795    });
 5796}
 5797
 5798#[gpui::test]
 5799async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5800    init_test(cx, |settings| {
 5801        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5802    });
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805
 5806    let language = Arc::new(Language::new(
 5807        LanguageConfig {
 5808            brackets: BracketPairConfig {
 5809                pairs: vec![
 5810                    BracketPair {
 5811                        start: "{".to_string(),
 5812                        end: "}".to_string(),
 5813                        close: true,
 5814                        surround: true,
 5815                        newline: true,
 5816                    },
 5817                    BracketPair {
 5818                        start: "(".to_string(),
 5819                        end: ")".to_string(),
 5820                        close: true,
 5821                        surround: true,
 5822                        newline: true,
 5823                    },
 5824                    BracketPair {
 5825                        start: "[".to_string(),
 5826                        end: "]".to_string(),
 5827                        close: false,
 5828                        surround: true,
 5829                        newline: true,
 5830                    },
 5831                ],
 5832                ..Default::default()
 5833            },
 5834            autoclose_before: "})]".to_string(),
 5835            ..Default::default()
 5836        },
 5837        Some(tree_sitter_rust::language()),
 5838    ));
 5839
 5840    cx.language_registry().add(language.clone());
 5841    cx.update_buffer(|buffer, cx| {
 5842        buffer.set_language(Some(language), cx);
 5843    });
 5844
 5845    cx.set_state(
 5846        &"
 5847            {(ˇ)}
 5848            [[ˇ]]
 5849            {(ˇ)}
 5850        "
 5851        .unindent(),
 5852    );
 5853
 5854    cx.update_editor(|view, cx| {
 5855        view.backspace(&Default::default(), cx);
 5856        view.backspace(&Default::default(), cx);
 5857    });
 5858
 5859    cx.assert_editor_state(
 5860        &"
 5861            ˇ
 5862            ˇ]]
 5863            ˇ
 5864        "
 5865        .unindent(),
 5866    );
 5867
 5868    cx.update_editor(|view, cx| {
 5869        view.handle_input("{", cx);
 5870        view.handle_input("{", cx);
 5871        view.move_right(&MoveRight, cx);
 5872        view.move_right(&MoveRight, cx);
 5873        view.move_left(&MoveLeft, cx);
 5874        view.move_left(&MoveLeft, cx);
 5875        view.backspace(&Default::default(), cx);
 5876    });
 5877
 5878    cx.assert_editor_state(
 5879        &"
 5880            {ˇ}
 5881            {ˇ}]]
 5882            {ˇ}
 5883        "
 5884        .unindent(),
 5885    );
 5886
 5887    cx.update_editor(|view, cx| {
 5888        view.backspace(&Default::default(), cx);
 5889    });
 5890
 5891    cx.assert_editor_state(
 5892        &"
 5893            ˇ
 5894            ˇ]]
 5895            ˇ
 5896        "
 5897        .unindent(),
 5898    );
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let language = Arc::new(Language::new(
 5906        LanguageConfig::default(),
 5907        Some(tree_sitter_rust::language()),
 5908    ));
 5909
 5910    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5911    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5912    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5913    editor
 5914        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5915        .await;
 5916
 5917    _ = editor.update(cx, |editor, cx| {
 5918        editor.set_auto_replace_emoji_shortcode(true);
 5919
 5920        editor.handle_input("Hello ", cx);
 5921        editor.handle_input(":wave", cx);
 5922        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5923
 5924        editor.handle_input(":", cx);
 5925        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5926
 5927        editor.handle_input(" :smile", cx);
 5928        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5929
 5930        editor.handle_input(":", cx);
 5931        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5932
 5933        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5934        editor.handle_input(":wave", cx);
 5935        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5936
 5937        editor.handle_input(":", cx);
 5938        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5939
 5940        editor.handle_input(":1", cx);
 5941        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5942
 5943        editor.handle_input(":", cx);
 5944        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5945
 5946        // Ensure shortcode does not get replaced when it is part of a word
 5947        editor.handle_input(" Test:wave", cx);
 5948        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5949
 5950        editor.handle_input(":", cx);
 5951        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5952
 5953        editor.set_auto_replace_emoji_shortcode(false);
 5954
 5955        // Ensure shortcode does not get replaced when auto replace is off
 5956        editor.handle_input(" :wave", cx);
 5957        assert_eq!(
 5958            editor.text(cx),
 5959            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5960        );
 5961
 5962        editor.handle_input(":", cx);
 5963        assert_eq!(
 5964            editor.text(cx),
 5965            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5966        );
 5967    });
 5968}
 5969
 5970#[gpui::test]
 5971async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5972    init_test(cx, |_| {});
 5973
 5974    let (text, insertion_ranges) = marked_text_ranges(
 5975        indoc! {"
 5976            a.ˇ b
 5977            a.ˇ b
 5978            a.ˇ b
 5979        "},
 5980        false,
 5981    );
 5982
 5983    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5984    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5985
 5986    _ = editor.update(cx, |editor, cx| {
 5987        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5988
 5989        editor
 5990            .insert_snippet(&insertion_ranges, snippet, cx)
 5991            .unwrap();
 5992
 5993        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5994            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5995            assert_eq!(editor.text(cx), expected_text);
 5996            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5997        }
 5998
 5999        assert(
 6000            editor,
 6001            cx,
 6002            indoc! {"
 6003                a.f(«one», two, «three») b
 6004                a.f(«one», two, «three») b
 6005                a.f(«one», two, «three») b
 6006            "},
 6007        );
 6008
 6009        // Can't move earlier than the first tab stop
 6010        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6011        assert(
 6012            editor,
 6013            cx,
 6014            indoc! {"
 6015                a.f(«one», two, «three») b
 6016                a.f(«one», two, «three») b
 6017                a.f(«one», two, «three») b
 6018            "},
 6019        );
 6020
 6021        assert!(editor.move_to_next_snippet_tabstop(cx));
 6022        assert(
 6023            editor,
 6024            cx,
 6025            indoc! {"
 6026                a.f(one, «two», three) b
 6027                a.f(one, «two», three) b
 6028                a.f(one, «two», three) b
 6029            "},
 6030        );
 6031
 6032        editor.move_to_prev_snippet_tabstop(cx);
 6033        assert(
 6034            editor,
 6035            cx,
 6036            indoc! {"
 6037                a.f(«one», two, «three») b
 6038                a.f(«one», two, «three») b
 6039                a.f(«one», two, «three») b
 6040            "},
 6041        );
 6042
 6043        assert!(editor.move_to_next_snippet_tabstop(cx));
 6044        assert(
 6045            editor,
 6046            cx,
 6047            indoc! {"
 6048                a.f(one, «two», three) b
 6049                a.f(one, «two», three) b
 6050                a.f(one, «two», three) b
 6051            "},
 6052        );
 6053        assert!(editor.move_to_next_snippet_tabstop(cx));
 6054        assert(
 6055            editor,
 6056            cx,
 6057            indoc! {"
 6058                a.f(one, two, three)ˇ b
 6059                a.f(one, two, three)ˇ b
 6060                a.f(one, two, three)ˇ b
 6061            "},
 6062        );
 6063
 6064        // As soon as the last tab stop is reached, snippet state is gone
 6065        editor.move_to_prev_snippet_tabstop(cx);
 6066        assert(
 6067            editor,
 6068            cx,
 6069            indoc! {"
 6070                a.f(one, two, three)ˇ b
 6071                a.f(one, two, three)ˇ b
 6072                a.f(one, two, three)ˇ b
 6073            "},
 6074        );
 6075    });
 6076}
 6077
 6078#[gpui::test]
 6079async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6080    init_test(cx, |_| {});
 6081
 6082    let fs = FakeFs::new(cx.executor());
 6083    fs.insert_file("/file.rs", Default::default()).await;
 6084
 6085    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6086
 6087    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6088    language_registry.add(rust_lang());
 6089    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6090        "Rust",
 6091        FakeLspAdapter {
 6092            capabilities: lsp::ServerCapabilities {
 6093                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6094                ..Default::default()
 6095            },
 6096            ..Default::default()
 6097        },
 6098    );
 6099
 6100    let buffer = project
 6101        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6102        .await
 6103        .unwrap();
 6104
 6105    cx.executor().start_waiting();
 6106    let fake_server = fake_servers.next().await.unwrap();
 6107
 6108    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6109    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6110    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6111    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6112
 6113    let save = editor
 6114        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6115        .unwrap();
 6116    fake_server
 6117        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6118            assert_eq!(
 6119                params.text_document.uri,
 6120                lsp::Url::from_file_path("/file.rs").unwrap()
 6121            );
 6122            assert_eq!(params.options.tab_size, 4);
 6123            Ok(Some(vec![lsp::TextEdit::new(
 6124                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6125                ", ".to_string(),
 6126            )]))
 6127        })
 6128        .next()
 6129        .await;
 6130    cx.executor().start_waiting();
 6131    save.await;
 6132
 6133    assert_eq!(
 6134        editor.update(cx, |editor, cx| editor.text(cx)),
 6135        "one, two\nthree\n"
 6136    );
 6137    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6138
 6139    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6140    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6141
 6142    // Ensure we can still save even if formatting hangs.
 6143    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6144        assert_eq!(
 6145            params.text_document.uri,
 6146            lsp::Url::from_file_path("/file.rs").unwrap()
 6147        );
 6148        futures::future::pending::<()>().await;
 6149        unreachable!()
 6150    });
 6151    let save = editor
 6152        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6153        .unwrap();
 6154    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6155    cx.executor().start_waiting();
 6156    save.await;
 6157    assert_eq!(
 6158        editor.update(cx, |editor, cx| editor.text(cx)),
 6159        "one\ntwo\nthree\n"
 6160    );
 6161    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6162
 6163    // For non-dirty buffer, no formatting request should be sent
 6164    let save = editor
 6165        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6166        .unwrap();
 6167    let _pending_format_request = fake_server
 6168        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6169            panic!("Should not be invoked on non-dirty buffer");
 6170        })
 6171        .next();
 6172    cx.executor().start_waiting();
 6173    save.await;
 6174
 6175    // Set rust language override and assert overridden tabsize is sent to language server
 6176    update_test_language_settings(cx, |settings| {
 6177        settings.languages.insert(
 6178            "Rust".into(),
 6179            LanguageSettingsContent {
 6180                tab_size: NonZeroU32::new(8),
 6181                ..Default::default()
 6182            },
 6183        );
 6184    });
 6185
 6186    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6187    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6188    let save = editor
 6189        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6190        .unwrap();
 6191    fake_server
 6192        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6193            assert_eq!(
 6194                params.text_document.uri,
 6195                lsp::Url::from_file_path("/file.rs").unwrap()
 6196            );
 6197            assert_eq!(params.options.tab_size, 8);
 6198            Ok(Some(vec![]))
 6199        })
 6200        .next()
 6201        .await;
 6202    cx.executor().start_waiting();
 6203    save.await;
 6204}
 6205
 6206#[gpui::test]
 6207async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6208    init_test(cx, |_| {});
 6209
 6210    let cols = 4;
 6211    let rows = 10;
 6212    let sample_text_1 = sample_text(rows, cols, 'a');
 6213    assert_eq!(
 6214        sample_text_1,
 6215        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6216    );
 6217    let sample_text_2 = sample_text(rows, cols, 'l');
 6218    assert_eq!(
 6219        sample_text_2,
 6220        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6221    );
 6222    let sample_text_3 = sample_text(rows, cols, 'v');
 6223    assert_eq!(
 6224        sample_text_3,
 6225        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6226    );
 6227
 6228    let fs = FakeFs::new(cx.executor());
 6229    fs.insert_tree(
 6230        "/a",
 6231        json!({
 6232            "main.rs": sample_text_1,
 6233            "other.rs": sample_text_2,
 6234            "lib.rs": sample_text_3,
 6235        }),
 6236    )
 6237    .await;
 6238
 6239    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6240    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6241    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6242
 6243    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6244    language_registry.add(rust_lang());
 6245    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6246        "Rust",
 6247        FakeLspAdapter {
 6248            capabilities: lsp::ServerCapabilities {
 6249                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6250                ..Default::default()
 6251            },
 6252            ..Default::default()
 6253        },
 6254    );
 6255
 6256    let worktree = project.update(cx, |project, _| {
 6257        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 6258        assert_eq!(worktrees.len(), 1);
 6259        worktrees.pop().unwrap()
 6260    });
 6261    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6262
 6263    let buffer_1 = project
 6264        .update(cx, |project, cx| {
 6265            project.open_buffer((worktree_id, "main.rs"), cx)
 6266        })
 6267        .await
 6268        .unwrap();
 6269    let buffer_2 = project
 6270        .update(cx, |project, cx| {
 6271            project.open_buffer((worktree_id, "other.rs"), cx)
 6272        })
 6273        .await
 6274        .unwrap();
 6275    let buffer_3 = project
 6276        .update(cx, |project, cx| {
 6277            project.open_buffer((worktree_id, "lib.rs"), cx)
 6278        })
 6279        .await
 6280        .unwrap();
 6281
 6282    let multi_buffer = cx.new_model(|cx| {
 6283        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6284        multi_buffer.push_excerpts(
 6285            buffer_1.clone(),
 6286            [
 6287                ExcerptRange {
 6288                    context: Point::new(0, 0)..Point::new(3, 0),
 6289                    primary: None,
 6290                },
 6291                ExcerptRange {
 6292                    context: Point::new(5, 0)..Point::new(7, 0),
 6293                    primary: None,
 6294                },
 6295                ExcerptRange {
 6296                    context: Point::new(9, 0)..Point::new(10, 4),
 6297                    primary: None,
 6298                },
 6299            ],
 6300            cx,
 6301        );
 6302        multi_buffer.push_excerpts(
 6303            buffer_2.clone(),
 6304            [
 6305                ExcerptRange {
 6306                    context: Point::new(0, 0)..Point::new(3, 0),
 6307                    primary: None,
 6308                },
 6309                ExcerptRange {
 6310                    context: Point::new(5, 0)..Point::new(7, 0),
 6311                    primary: None,
 6312                },
 6313                ExcerptRange {
 6314                    context: Point::new(9, 0)..Point::new(10, 4),
 6315                    primary: None,
 6316                },
 6317            ],
 6318            cx,
 6319        );
 6320        multi_buffer.push_excerpts(
 6321            buffer_3.clone(),
 6322            [
 6323                ExcerptRange {
 6324                    context: Point::new(0, 0)..Point::new(3, 0),
 6325                    primary: None,
 6326                },
 6327                ExcerptRange {
 6328                    context: Point::new(5, 0)..Point::new(7, 0),
 6329                    primary: None,
 6330                },
 6331                ExcerptRange {
 6332                    context: Point::new(9, 0)..Point::new(10, 4),
 6333                    primary: None,
 6334                },
 6335            ],
 6336            cx,
 6337        );
 6338        multi_buffer
 6339    });
 6340    let multi_buffer_editor = cx.new_view(|cx| {
 6341        Editor::new(
 6342            EditorMode::Full,
 6343            multi_buffer,
 6344            Some(project.clone()),
 6345            true,
 6346            cx,
 6347        )
 6348    });
 6349
 6350    multi_buffer_editor.update(cx, |editor, cx| {
 6351        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6352        editor.insert("|one|two|three|", cx);
 6353    });
 6354    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6355    multi_buffer_editor.update(cx, |editor, cx| {
 6356        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6357            s.select_ranges(Some(60..70))
 6358        });
 6359        editor.insert("|four|five|six|", cx);
 6360    });
 6361    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6362
 6363    // First two buffers should be edited, but not the third one.
 6364    assert_eq!(
 6365        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6366        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6367    );
 6368    buffer_1.update(cx, |buffer, _| {
 6369        assert!(buffer.is_dirty());
 6370        assert_eq!(
 6371            buffer.text(),
 6372            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6373        )
 6374    });
 6375    buffer_2.update(cx, |buffer, _| {
 6376        assert!(buffer.is_dirty());
 6377        assert_eq!(
 6378            buffer.text(),
 6379            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6380        )
 6381    });
 6382    buffer_3.update(cx, |buffer, _| {
 6383        assert!(!buffer.is_dirty());
 6384        assert_eq!(buffer.text(), sample_text_3,)
 6385    });
 6386
 6387    cx.executor().start_waiting();
 6388    let save = multi_buffer_editor
 6389        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6390        .unwrap();
 6391
 6392    let fake_server = fake_servers.next().await.unwrap();
 6393    fake_server
 6394        .server
 6395        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6396            Ok(Some(vec![lsp::TextEdit::new(
 6397                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6398                format!("[{} formatted]", params.text_document.uri),
 6399            )]))
 6400        })
 6401        .detach();
 6402    save.await;
 6403
 6404    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6405    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6406    assert_eq!(
 6407        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6408        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6409    );
 6410    buffer_1.update(cx, |buffer, _| {
 6411        assert!(!buffer.is_dirty());
 6412        assert_eq!(
 6413            buffer.text(),
 6414            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6415        )
 6416    });
 6417    buffer_2.update(cx, |buffer, _| {
 6418        assert!(!buffer.is_dirty());
 6419        assert_eq!(
 6420            buffer.text(),
 6421            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6422        )
 6423    });
 6424    buffer_3.update(cx, |buffer, _| {
 6425        assert!(!buffer.is_dirty());
 6426        assert_eq!(buffer.text(), sample_text_3,)
 6427    });
 6428}
 6429
 6430#[gpui::test]
 6431async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6432    init_test(cx, |_| {});
 6433
 6434    let fs = FakeFs::new(cx.executor());
 6435    fs.insert_file("/file.rs", Default::default()).await;
 6436
 6437    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6438
 6439    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6440    language_registry.add(rust_lang());
 6441    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6442        "Rust",
 6443        FakeLspAdapter {
 6444            capabilities: lsp::ServerCapabilities {
 6445                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6446                ..Default::default()
 6447            },
 6448            ..Default::default()
 6449        },
 6450    );
 6451
 6452    let buffer = project
 6453        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6454        .await
 6455        .unwrap();
 6456
 6457    cx.executor().start_waiting();
 6458    let fake_server = fake_servers.next().await.unwrap();
 6459
 6460    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6461    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6462    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6463    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6464
 6465    let save = editor
 6466        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6467        .unwrap();
 6468    fake_server
 6469        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6470            assert_eq!(
 6471                params.text_document.uri,
 6472                lsp::Url::from_file_path("/file.rs").unwrap()
 6473            );
 6474            assert_eq!(params.options.tab_size, 4);
 6475            Ok(Some(vec![lsp::TextEdit::new(
 6476                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6477                ", ".to_string(),
 6478            )]))
 6479        })
 6480        .next()
 6481        .await;
 6482    cx.executor().start_waiting();
 6483    save.await;
 6484    assert_eq!(
 6485        editor.update(cx, |editor, cx| editor.text(cx)),
 6486        "one, two\nthree\n"
 6487    );
 6488    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6489
 6490    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6491    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6492
 6493    // Ensure we can still save even if formatting hangs.
 6494    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6495        move |params, _| async move {
 6496            assert_eq!(
 6497                params.text_document.uri,
 6498                lsp::Url::from_file_path("/file.rs").unwrap()
 6499            );
 6500            futures::future::pending::<()>().await;
 6501            unreachable!()
 6502        },
 6503    );
 6504    let save = editor
 6505        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6506        .unwrap();
 6507    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6508    cx.executor().start_waiting();
 6509    save.await;
 6510    assert_eq!(
 6511        editor.update(cx, |editor, cx| editor.text(cx)),
 6512        "one\ntwo\nthree\n"
 6513    );
 6514    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6515
 6516    // For non-dirty buffer, no formatting request should be sent
 6517    let save = editor
 6518        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6519        .unwrap();
 6520    let _pending_format_request = fake_server
 6521        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6522            panic!("Should not be invoked on non-dirty buffer");
 6523        })
 6524        .next();
 6525    cx.executor().start_waiting();
 6526    save.await;
 6527
 6528    // Set Rust language override and assert overridden tabsize is sent to language server
 6529    update_test_language_settings(cx, |settings| {
 6530        settings.languages.insert(
 6531            "Rust".into(),
 6532            LanguageSettingsContent {
 6533                tab_size: NonZeroU32::new(8),
 6534                ..Default::default()
 6535            },
 6536        );
 6537    });
 6538
 6539    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6540    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6541    let save = editor
 6542        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6543        .unwrap();
 6544    fake_server
 6545        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6546            assert_eq!(
 6547                params.text_document.uri,
 6548                lsp::Url::from_file_path("/file.rs").unwrap()
 6549            );
 6550            assert_eq!(params.options.tab_size, 8);
 6551            Ok(Some(vec![]))
 6552        })
 6553        .next()
 6554        .await;
 6555    cx.executor().start_waiting();
 6556    save.await;
 6557}
 6558
 6559#[gpui::test]
 6560async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6561    init_test(cx, |settings| {
 6562        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6563    });
 6564
 6565    let fs = FakeFs::new(cx.executor());
 6566    fs.insert_file("/file.rs", Default::default()).await;
 6567
 6568    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6569
 6570    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6571    language_registry.add(Arc::new(Language::new(
 6572        LanguageConfig {
 6573            name: "Rust".into(),
 6574            matcher: LanguageMatcher {
 6575                path_suffixes: vec!["rs".to_string()],
 6576                ..Default::default()
 6577            },
 6578            ..LanguageConfig::default()
 6579        },
 6580        Some(tree_sitter_rust::language()),
 6581    )));
 6582    update_test_language_settings(cx, |settings| {
 6583        // Enable Prettier formatting for the same buffer, and ensure
 6584        // LSP is called instead of Prettier.
 6585        settings.defaults.prettier = Some(PrettierSettings {
 6586            allowed: true,
 6587            ..PrettierSettings::default()
 6588        });
 6589    });
 6590    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6591        "Rust",
 6592        FakeLspAdapter {
 6593            capabilities: lsp::ServerCapabilities {
 6594                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6595                ..Default::default()
 6596            },
 6597            ..Default::default()
 6598        },
 6599    );
 6600
 6601    let buffer = project
 6602        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6603        .await
 6604        .unwrap();
 6605
 6606    cx.executor().start_waiting();
 6607    let fake_server = fake_servers.next().await.unwrap();
 6608
 6609    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6610    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6611    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6612
 6613    let format = editor
 6614        .update(cx, |editor, cx| {
 6615            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6616        })
 6617        .unwrap();
 6618    fake_server
 6619        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6620            assert_eq!(
 6621                params.text_document.uri,
 6622                lsp::Url::from_file_path("/file.rs").unwrap()
 6623            );
 6624            assert_eq!(params.options.tab_size, 4);
 6625            Ok(Some(vec![lsp::TextEdit::new(
 6626                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6627                ", ".to_string(),
 6628            )]))
 6629        })
 6630        .next()
 6631        .await;
 6632    cx.executor().start_waiting();
 6633    format.await;
 6634    assert_eq!(
 6635        editor.update(cx, |editor, cx| editor.text(cx)),
 6636        "one, two\nthree\n"
 6637    );
 6638
 6639    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6640    // Ensure we don't lock if formatting hangs.
 6641    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6642        assert_eq!(
 6643            params.text_document.uri,
 6644            lsp::Url::from_file_path("/file.rs").unwrap()
 6645        );
 6646        futures::future::pending::<()>().await;
 6647        unreachable!()
 6648    });
 6649    let format = editor
 6650        .update(cx, |editor, cx| {
 6651            editor.perform_format(project, FormatTrigger::Manual, cx)
 6652        })
 6653        .unwrap();
 6654    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6655    cx.executor().start_waiting();
 6656    format.await;
 6657    assert_eq!(
 6658        editor.update(cx, |editor, cx| editor.text(cx)),
 6659        "one\ntwo\nthree\n"
 6660    );
 6661}
 6662
 6663#[gpui::test]
 6664async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6665    init_test(cx, |_| {});
 6666
 6667    let mut cx = EditorLspTestContext::new_rust(
 6668        lsp::ServerCapabilities {
 6669            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6670            ..Default::default()
 6671        },
 6672        cx,
 6673    )
 6674    .await;
 6675
 6676    cx.set_state(indoc! {"
 6677        one.twoˇ
 6678    "});
 6679
 6680    // The format request takes a long time. When it completes, it inserts
 6681    // a newline and an indent before the `.`
 6682    cx.lsp
 6683        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6684            let executor = cx.background_executor().clone();
 6685            async move {
 6686                executor.timer(Duration::from_millis(100)).await;
 6687                Ok(Some(vec![lsp::TextEdit {
 6688                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6689                    new_text: "\n    ".into(),
 6690                }]))
 6691            }
 6692        });
 6693
 6694    // Submit a format request.
 6695    let format_1 = cx
 6696        .update_editor(|editor, cx| editor.format(&Format, cx))
 6697        .unwrap();
 6698    cx.executor().run_until_parked();
 6699
 6700    // Submit a second format request.
 6701    let format_2 = cx
 6702        .update_editor(|editor, cx| editor.format(&Format, cx))
 6703        .unwrap();
 6704    cx.executor().run_until_parked();
 6705
 6706    // Wait for both format requests to complete
 6707    cx.executor().advance_clock(Duration::from_millis(200));
 6708    cx.executor().start_waiting();
 6709    format_1.await.unwrap();
 6710    cx.executor().start_waiting();
 6711    format_2.await.unwrap();
 6712
 6713    // The formatting edits only happens once.
 6714    cx.assert_editor_state(indoc! {"
 6715        one
 6716            .twoˇ
 6717    "});
 6718}
 6719
 6720#[gpui::test]
 6721async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6722    init_test(cx, |settings| {
 6723        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6724    });
 6725
 6726    let mut cx = EditorLspTestContext::new_rust(
 6727        lsp::ServerCapabilities {
 6728            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6729            ..Default::default()
 6730        },
 6731        cx,
 6732    )
 6733    .await;
 6734
 6735    // Set up a buffer white some trailing whitespace and no trailing newline.
 6736    cx.set_state(
 6737        &[
 6738            "one ",   //
 6739            "twoˇ",   //
 6740            "three ", //
 6741            "four",   //
 6742        ]
 6743        .join("\n"),
 6744    );
 6745
 6746    // Submit a format request.
 6747    let format = cx
 6748        .update_editor(|editor, cx| editor.format(&Format, cx))
 6749        .unwrap();
 6750
 6751    // Record which buffer changes have been sent to the language server
 6752    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6753    cx.lsp
 6754        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6755            let buffer_changes = buffer_changes.clone();
 6756            move |params, _| {
 6757                buffer_changes.lock().extend(
 6758                    params
 6759                        .content_changes
 6760                        .into_iter()
 6761                        .map(|e| (e.range.unwrap(), e.text)),
 6762                );
 6763            }
 6764        });
 6765
 6766    // Handle formatting requests to the language server.
 6767    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6768        let buffer_changes = buffer_changes.clone();
 6769        move |_, _| {
 6770            // When formatting is requested, trailing whitespace has already been stripped,
 6771            // and the trailing newline has already been added.
 6772            assert_eq!(
 6773                &buffer_changes.lock()[1..],
 6774                &[
 6775                    (
 6776                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6777                        "".into()
 6778                    ),
 6779                    (
 6780                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6781                        "".into()
 6782                    ),
 6783                    (
 6784                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6785                        "\n".into()
 6786                    ),
 6787                ]
 6788            );
 6789
 6790            // Insert blank lines between each line of the buffer.
 6791            async move {
 6792                Ok(Some(vec![
 6793                    lsp::TextEdit {
 6794                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6795                        new_text: "\n".into(),
 6796                    },
 6797                    lsp::TextEdit {
 6798                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6799                        new_text: "\n".into(),
 6800                    },
 6801                ]))
 6802            }
 6803        }
 6804    });
 6805
 6806    // After formatting the buffer, the trailing whitespace is stripped,
 6807    // a newline is appended, and the edits provided by the language server
 6808    // have been applied.
 6809    format.await.unwrap();
 6810    cx.assert_editor_state(
 6811        &[
 6812            "one",   //
 6813            "",      //
 6814            "twoˇ",  //
 6815            "",      //
 6816            "three", //
 6817            "four",  //
 6818            "",      //
 6819        ]
 6820        .join("\n"),
 6821    );
 6822
 6823    // Undoing the formatting undoes the trailing whitespace removal, the
 6824    // trailing newline, and the LSP edits.
 6825    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6826    cx.assert_editor_state(
 6827        &[
 6828            "one ",   //
 6829            "twoˇ",   //
 6830            "three ", //
 6831            "four",   //
 6832        ]
 6833        .join("\n"),
 6834    );
 6835}
 6836
 6837#[gpui::test]
 6838async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6839    cx: &mut gpui::TestAppContext,
 6840) {
 6841    init_test(cx, |_| {});
 6842
 6843    cx.update(|cx| {
 6844        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6845            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6846                settings.auto_signature_help = Some(true);
 6847            });
 6848        });
 6849    });
 6850
 6851    let mut cx = EditorLspTestContext::new_rust(
 6852        lsp::ServerCapabilities {
 6853            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6854                ..Default::default()
 6855            }),
 6856            ..Default::default()
 6857        },
 6858        cx,
 6859    )
 6860    .await;
 6861
 6862    let language = Language::new(
 6863        LanguageConfig {
 6864            name: "Rust".into(),
 6865            brackets: BracketPairConfig {
 6866                pairs: vec![
 6867                    BracketPair {
 6868                        start: "{".to_string(),
 6869                        end: "}".to_string(),
 6870                        close: true,
 6871                        surround: true,
 6872                        newline: true,
 6873                    },
 6874                    BracketPair {
 6875                        start: "(".to_string(),
 6876                        end: ")".to_string(),
 6877                        close: true,
 6878                        surround: true,
 6879                        newline: true,
 6880                    },
 6881                    BracketPair {
 6882                        start: "/*".to_string(),
 6883                        end: " */".to_string(),
 6884                        close: true,
 6885                        surround: true,
 6886                        newline: true,
 6887                    },
 6888                    BracketPair {
 6889                        start: "[".to_string(),
 6890                        end: "]".to_string(),
 6891                        close: false,
 6892                        surround: false,
 6893                        newline: true,
 6894                    },
 6895                    BracketPair {
 6896                        start: "\"".to_string(),
 6897                        end: "\"".to_string(),
 6898                        close: true,
 6899                        surround: true,
 6900                        newline: false,
 6901                    },
 6902                    BracketPair {
 6903                        start: "<".to_string(),
 6904                        end: ">".to_string(),
 6905                        close: false,
 6906                        surround: true,
 6907                        newline: true,
 6908                    },
 6909                ],
 6910                ..Default::default()
 6911            },
 6912            autoclose_before: "})]".to_string(),
 6913            ..Default::default()
 6914        },
 6915        Some(tree_sitter_rust::language()),
 6916    );
 6917    let language = Arc::new(language);
 6918
 6919    cx.language_registry().add(language.clone());
 6920    cx.update_buffer(|buffer, cx| {
 6921        buffer.set_language(Some(language), cx);
 6922    });
 6923
 6924    cx.set_state(
 6925        &r#"
 6926            fn main() {
 6927                sampleˇ
 6928            }
 6929        "#
 6930        .unindent(),
 6931    );
 6932
 6933    cx.update_editor(|view, cx| {
 6934        view.handle_input("(", cx);
 6935    });
 6936    cx.assert_editor_state(
 6937        &"
 6938            fn main() {
 6939                sample(ˇ)
 6940            }
 6941        "
 6942        .unindent(),
 6943    );
 6944
 6945    let mocked_response = lsp::SignatureHelp {
 6946        signatures: vec![lsp::SignatureInformation {
 6947            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6948            documentation: None,
 6949            parameters: Some(vec![
 6950                lsp::ParameterInformation {
 6951                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6952                    documentation: None,
 6953                },
 6954                lsp::ParameterInformation {
 6955                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6956                    documentation: None,
 6957                },
 6958            ]),
 6959            active_parameter: None,
 6960        }],
 6961        active_signature: Some(0),
 6962        active_parameter: Some(0),
 6963    };
 6964    handle_signature_help_request(&mut cx, mocked_response).await;
 6965
 6966    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 6967        .await;
 6968
 6969    cx.editor(|editor, _| {
 6970        let signature_help_state = editor.signature_help_state.popover().cloned();
 6971        assert!(signature_help_state.is_some());
 6972        let ParsedMarkdown {
 6973            text, highlights, ..
 6974        } = signature_help_state.unwrap().parsed_content;
 6975        assert_eq!(text, "param1: u8, param2: u8");
 6976        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 6977    });
 6978}
 6979
 6980#[gpui::test]
 6981async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 6982    init_test(cx, |_| {});
 6983
 6984    cx.update(|cx| {
 6985        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6986            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6987                settings.auto_signature_help = Some(false);
 6988                settings.show_signature_help_after_edits = Some(false);
 6989            });
 6990        });
 6991    });
 6992
 6993    let mut cx = EditorLspTestContext::new_rust(
 6994        lsp::ServerCapabilities {
 6995            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6996                ..Default::default()
 6997            }),
 6998            ..Default::default()
 6999        },
 7000        cx,
 7001    )
 7002    .await;
 7003
 7004    let language = Language::new(
 7005        LanguageConfig {
 7006            name: "Rust".into(),
 7007            brackets: BracketPairConfig {
 7008                pairs: vec![
 7009                    BracketPair {
 7010                        start: "{".to_string(),
 7011                        end: "}".to_string(),
 7012                        close: true,
 7013                        surround: true,
 7014                        newline: true,
 7015                    },
 7016                    BracketPair {
 7017                        start: "(".to_string(),
 7018                        end: ")".to_string(),
 7019                        close: true,
 7020                        surround: true,
 7021                        newline: true,
 7022                    },
 7023                    BracketPair {
 7024                        start: "/*".to_string(),
 7025                        end: " */".to_string(),
 7026                        close: true,
 7027                        surround: true,
 7028                        newline: true,
 7029                    },
 7030                    BracketPair {
 7031                        start: "[".to_string(),
 7032                        end: "]".to_string(),
 7033                        close: false,
 7034                        surround: false,
 7035                        newline: true,
 7036                    },
 7037                    BracketPair {
 7038                        start: "\"".to_string(),
 7039                        end: "\"".to_string(),
 7040                        close: true,
 7041                        surround: true,
 7042                        newline: false,
 7043                    },
 7044                    BracketPair {
 7045                        start: "<".to_string(),
 7046                        end: ">".to_string(),
 7047                        close: false,
 7048                        surround: true,
 7049                        newline: true,
 7050                    },
 7051                ],
 7052                ..Default::default()
 7053            },
 7054            autoclose_before: "})]".to_string(),
 7055            ..Default::default()
 7056        },
 7057        Some(tree_sitter_rust::language()),
 7058    );
 7059    let language = Arc::new(language);
 7060
 7061    cx.language_registry().add(language.clone());
 7062    cx.update_buffer(|buffer, cx| {
 7063        buffer.set_language(Some(language), cx);
 7064    });
 7065
 7066    // Ensure that signature_help is not called when no signature help is enabled.
 7067    cx.set_state(
 7068        &r#"
 7069            fn main() {
 7070                sampleˇ
 7071            }
 7072        "#
 7073        .unindent(),
 7074    );
 7075    cx.update_editor(|view, cx| {
 7076        view.handle_input("(", cx);
 7077    });
 7078    cx.assert_editor_state(
 7079        &"
 7080            fn main() {
 7081                sample(ˇ)
 7082            }
 7083        "
 7084        .unindent(),
 7085    );
 7086    cx.editor(|editor, _| {
 7087        assert!(editor.signature_help_state.task().is_none());
 7088    });
 7089
 7090    let mocked_response = lsp::SignatureHelp {
 7091        signatures: vec![lsp::SignatureInformation {
 7092            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7093            documentation: None,
 7094            parameters: Some(vec![
 7095                lsp::ParameterInformation {
 7096                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7097                    documentation: None,
 7098                },
 7099                lsp::ParameterInformation {
 7100                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7101                    documentation: None,
 7102                },
 7103            ]),
 7104            active_parameter: None,
 7105        }],
 7106        active_signature: Some(0),
 7107        active_parameter: Some(0),
 7108    };
 7109
 7110    // Ensure that signature_help is called when enabled afte edits
 7111    cx.update(|cx| {
 7112        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7113            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7114                settings.auto_signature_help = Some(false);
 7115                settings.show_signature_help_after_edits = Some(true);
 7116            });
 7117        });
 7118    });
 7119    cx.set_state(
 7120        &r#"
 7121            fn main() {
 7122                sampleˇ
 7123            }
 7124        "#
 7125        .unindent(),
 7126    );
 7127    cx.update_editor(|view, cx| {
 7128        view.handle_input("(", cx);
 7129    });
 7130    cx.assert_editor_state(
 7131        &"
 7132            fn main() {
 7133                sample(ˇ)
 7134            }
 7135        "
 7136        .unindent(),
 7137    );
 7138    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7139    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7140        .await;
 7141    cx.update_editor(|editor, _| {
 7142        let signature_help_state = editor.signature_help_state.popover().cloned();
 7143        assert!(signature_help_state.is_some());
 7144        let ParsedMarkdown {
 7145            text, highlights, ..
 7146        } = signature_help_state.unwrap().parsed_content;
 7147        assert_eq!(text, "param1: u8, param2: u8");
 7148        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7149        editor.signature_help_state = SignatureHelpState::default();
 7150    });
 7151
 7152    // Ensure that signature_help is called when auto signature help override is enabled
 7153    cx.update(|cx| {
 7154        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7155            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7156                settings.auto_signature_help = Some(true);
 7157                settings.show_signature_help_after_edits = Some(false);
 7158            });
 7159        });
 7160    });
 7161    cx.set_state(
 7162        &r#"
 7163            fn main() {
 7164                sampleˇ
 7165            }
 7166        "#
 7167        .unindent(),
 7168    );
 7169    cx.update_editor(|view, cx| {
 7170        view.handle_input("(", cx);
 7171    });
 7172    cx.assert_editor_state(
 7173        &"
 7174            fn main() {
 7175                sample(ˇ)
 7176            }
 7177        "
 7178        .unindent(),
 7179    );
 7180    handle_signature_help_request(&mut cx, mocked_response).await;
 7181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7182        .await;
 7183    cx.editor(|editor, _| {
 7184        let signature_help_state = editor.signature_help_state.popover().cloned();
 7185        assert!(signature_help_state.is_some());
 7186        let ParsedMarkdown {
 7187            text, highlights, ..
 7188        } = signature_help_state.unwrap().parsed_content;
 7189        assert_eq!(text, "param1: u8, param2: u8");
 7190        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7191    });
 7192}
 7193
 7194#[gpui::test]
 7195async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7196    init_test(cx, |_| {});
 7197    cx.update(|cx| {
 7198        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7199            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7200                settings.auto_signature_help = Some(true);
 7201            });
 7202        });
 7203    });
 7204
 7205    let mut cx = EditorLspTestContext::new_rust(
 7206        lsp::ServerCapabilities {
 7207            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7208                ..Default::default()
 7209            }),
 7210            ..Default::default()
 7211        },
 7212        cx,
 7213    )
 7214    .await;
 7215
 7216    // A test that directly calls `show_signature_help`
 7217    cx.update_editor(|editor, cx| {
 7218        editor.show_signature_help(&ShowSignatureHelp, cx);
 7219    });
 7220
 7221    let mocked_response = lsp::SignatureHelp {
 7222        signatures: vec![lsp::SignatureInformation {
 7223            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7224            documentation: None,
 7225            parameters: Some(vec![
 7226                lsp::ParameterInformation {
 7227                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7228                    documentation: None,
 7229                },
 7230                lsp::ParameterInformation {
 7231                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7232                    documentation: None,
 7233                },
 7234            ]),
 7235            active_parameter: None,
 7236        }],
 7237        active_signature: Some(0),
 7238        active_parameter: Some(0),
 7239    };
 7240    handle_signature_help_request(&mut cx, mocked_response).await;
 7241
 7242    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7243        .await;
 7244
 7245    cx.editor(|editor, _| {
 7246        let signature_help_state = editor.signature_help_state.popover().cloned();
 7247        assert!(signature_help_state.is_some());
 7248        let ParsedMarkdown {
 7249            text, highlights, ..
 7250        } = signature_help_state.unwrap().parsed_content;
 7251        assert_eq!(text, "param1: u8, param2: u8");
 7252        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7253    });
 7254
 7255    // When exiting outside from inside the brackets, `signature_help` is closed.
 7256    cx.set_state(indoc! {"
 7257        fn main() {
 7258            sample(ˇ);
 7259        }
 7260
 7261        fn sample(param1: u8, param2: u8) {}
 7262    "});
 7263
 7264    cx.update_editor(|editor, cx| {
 7265        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7266    });
 7267
 7268    let mocked_response = lsp::SignatureHelp {
 7269        signatures: Vec::new(),
 7270        active_signature: None,
 7271        active_parameter: None,
 7272    };
 7273    handle_signature_help_request(&mut cx, mocked_response).await;
 7274
 7275    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7276        .await;
 7277
 7278    cx.editor(|editor, _| {
 7279        assert!(!editor.signature_help_state.is_shown());
 7280    });
 7281
 7282    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7283    cx.set_state(indoc! {"
 7284        fn main() {
 7285            sample(ˇ);
 7286        }
 7287
 7288        fn sample(param1: u8, param2: u8) {}
 7289    "});
 7290
 7291    let mocked_response = lsp::SignatureHelp {
 7292        signatures: vec![lsp::SignatureInformation {
 7293            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7294            documentation: None,
 7295            parameters: Some(vec![
 7296                lsp::ParameterInformation {
 7297                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7298                    documentation: None,
 7299                },
 7300                lsp::ParameterInformation {
 7301                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7302                    documentation: None,
 7303                },
 7304            ]),
 7305            active_parameter: None,
 7306        }],
 7307        active_signature: Some(0),
 7308        active_parameter: Some(0),
 7309    };
 7310    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7311    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7312        .await;
 7313    cx.editor(|editor, _| {
 7314        assert!(editor.signature_help_state.is_shown());
 7315    });
 7316
 7317    // Restore the popover with more parameter input
 7318    cx.set_state(indoc! {"
 7319        fn main() {
 7320            sample(param1, param2ˇ);
 7321        }
 7322
 7323        fn sample(param1: u8, param2: u8) {}
 7324    "});
 7325
 7326    let mocked_response = lsp::SignatureHelp {
 7327        signatures: vec![lsp::SignatureInformation {
 7328            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7329            documentation: None,
 7330            parameters: Some(vec![
 7331                lsp::ParameterInformation {
 7332                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7333                    documentation: None,
 7334                },
 7335                lsp::ParameterInformation {
 7336                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7337                    documentation: None,
 7338                },
 7339            ]),
 7340            active_parameter: None,
 7341        }],
 7342        active_signature: Some(0),
 7343        active_parameter: Some(1),
 7344    };
 7345    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7346    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7347        .await;
 7348
 7349    // When selecting a range, the popover is gone.
 7350    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7351    cx.update_editor(|editor, cx| {
 7352        editor.change_selections(None, cx, |s| {
 7353            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7354        })
 7355    });
 7356    cx.assert_editor_state(indoc! {"
 7357        fn main() {
 7358            sample(param1, «ˇparam2»);
 7359        }
 7360
 7361        fn sample(param1: u8, param2: u8) {}
 7362    "});
 7363    cx.editor(|editor, _| {
 7364        assert!(!editor.signature_help_state.is_shown());
 7365    });
 7366
 7367    // When unselecting again, the popover is back if within the brackets.
 7368    cx.update_editor(|editor, cx| {
 7369        editor.change_selections(None, cx, |s| {
 7370            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7371        })
 7372    });
 7373    cx.assert_editor_state(indoc! {"
 7374        fn main() {
 7375            sample(param1, ˇparam2);
 7376        }
 7377
 7378        fn sample(param1: u8, param2: u8) {}
 7379    "});
 7380    handle_signature_help_request(&mut cx, mocked_response).await;
 7381    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7382        .await;
 7383    cx.editor(|editor, _| {
 7384        assert!(editor.signature_help_state.is_shown());
 7385    });
 7386
 7387    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7388    cx.update_editor(|editor, cx| {
 7389        editor.change_selections(None, cx, |s| {
 7390            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7391            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7392        })
 7393    });
 7394    cx.assert_editor_state(indoc! {"
 7395        fn main() {
 7396            sample(param1, ˇparam2);
 7397        }
 7398
 7399        fn sample(param1: u8, param2: u8) {}
 7400    "});
 7401
 7402    let mocked_response = lsp::SignatureHelp {
 7403        signatures: vec![lsp::SignatureInformation {
 7404            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7405            documentation: None,
 7406            parameters: Some(vec![
 7407                lsp::ParameterInformation {
 7408                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7409                    documentation: None,
 7410                },
 7411                lsp::ParameterInformation {
 7412                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7413                    documentation: None,
 7414                },
 7415            ]),
 7416            active_parameter: None,
 7417        }],
 7418        active_signature: Some(0),
 7419        active_parameter: Some(1),
 7420    };
 7421    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7422    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7423        .await;
 7424    cx.update_editor(|editor, cx| {
 7425        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7426    });
 7427    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7428        .await;
 7429    cx.update_editor(|editor, cx| {
 7430        editor.change_selections(None, cx, |s| {
 7431            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7432        })
 7433    });
 7434    cx.assert_editor_state(indoc! {"
 7435        fn main() {
 7436            sample(param1, «ˇparam2»);
 7437        }
 7438
 7439        fn sample(param1: u8, param2: u8) {}
 7440    "});
 7441    cx.update_editor(|editor, cx| {
 7442        editor.change_selections(None, cx, |s| {
 7443            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7444        })
 7445    });
 7446    cx.assert_editor_state(indoc! {"
 7447        fn main() {
 7448            sample(param1, ˇparam2);
 7449        }
 7450
 7451        fn sample(param1: u8, param2: u8) {}
 7452    "});
 7453    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7454        .await;
 7455}
 7456
 7457#[gpui::test]
 7458async fn test_completion(cx: &mut gpui::TestAppContext) {
 7459    init_test(cx, |_| {});
 7460
 7461    let mut cx = EditorLspTestContext::new_rust(
 7462        lsp::ServerCapabilities {
 7463            completion_provider: Some(lsp::CompletionOptions {
 7464                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7465                resolve_provider: Some(true),
 7466                ..Default::default()
 7467            }),
 7468            ..Default::default()
 7469        },
 7470        cx,
 7471    )
 7472    .await;
 7473    let counter = Arc::new(AtomicUsize::new(0));
 7474
 7475    cx.set_state(indoc! {"
 7476        oneˇ
 7477        two
 7478        three
 7479    "});
 7480    cx.simulate_keystroke(".");
 7481    handle_completion_request(
 7482        &mut cx,
 7483        indoc! {"
 7484            one.|<>
 7485            two
 7486            three
 7487        "},
 7488        vec!["first_completion", "second_completion"],
 7489        counter.clone(),
 7490    )
 7491    .await;
 7492    cx.condition(|editor, _| editor.context_menu_visible())
 7493        .await;
 7494    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7495
 7496    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7497        editor.context_menu_next(&Default::default(), cx);
 7498        editor
 7499            .confirm_completion(&ConfirmCompletion::default(), cx)
 7500            .unwrap()
 7501    });
 7502    cx.assert_editor_state(indoc! {"
 7503        one.second_completionˇ
 7504        two
 7505        three
 7506    "});
 7507
 7508    handle_resolve_completion_request(
 7509        &mut cx,
 7510        Some(vec![
 7511            (
 7512                //This overlaps with the primary completion edit which is
 7513                //misbehavior from the LSP spec, test that we filter it out
 7514                indoc! {"
 7515                    one.second_ˇcompletion
 7516                    two
 7517                    threeˇ
 7518                "},
 7519                "overlapping additional edit",
 7520            ),
 7521            (
 7522                indoc! {"
 7523                    one.second_completion
 7524                    two
 7525                    threeˇ
 7526                "},
 7527                "\nadditional edit",
 7528            ),
 7529        ]),
 7530    )
 7531    .await;
 7532    apply_additional_edits.await.unwrap();
 7533    cx.assert_editor_state(indoc! {"
 7534        one.second_completionˇ
 7535        two
 7536        three
 7537        additional edit
 7538    "});
 7539
 7540    cx.set_state(indoc! {"
 7541        one.second_completion
 7542        twoˇ
 7543        threeˇ
 7544        additional edit
 7545    "});
 7546    cx.simulate_keystroke(" ");
 7547    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7548    cx.simulate_keystroke("s");
 7549    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7550
 7551    cx.assert_editor_state(indoc! {"
 7552        one.second_completion
 7553        two sˇ
 7554        three sˇ
 7555        additional edit
 7556    "});
 7557    handle_completion_request(
 7558        &mut cx,
 7559        indoc! {"
 7560            one.second_completion
 7561            two s
 7562            three <s|>
 7563            additional edit
 7564        "},
 7565        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7566        counter.clone(),
 7567    )
 7568    .await;
 7569    cx.condition(|editor, _| editor.context_menu_visible())
 7570        .await;
 7571    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7572
 7573    cx.simulate_keystroke("i");
 7574
 7575    handle_completion_request(
 7576        &mut cx,
 7577        indoc! {"
 7578            one.second_completion
 7579            two si
 7580            three <si|>
 7581            additional edit
 7582        "},
 7583        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7584        counter.clone(),
 7585    )
 7586    .await;
 7587    cx.condition(|editor, _| editor.context_menu_visible())
 7588        .await;
 7589    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7590
 7591    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7592        editor
 7593            .confirm_completion(&ConfirmCompletion::default(), cx)
 7594            .unwrap()
 7595    });
 7596    cx.assert_editor_state(indoc! {"
 7597        one.second_completion
 7598        two sixth_completionˇ
 7599        three sixth_completionˇ
 7600        additional edit
 7601    "});
 7602
 7603    handle_resolve_completion_request(&mut cx, None).await;
 7604    apply_additional_edits.await.unwrap();
 7605
 7606    _ = cx.update(|cx| {
 7607        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7608            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7609                settings.show_completions_on_input = Some(false);
 7610            });
 7611        })
 7612    });
 7613    cx.set_state("editorˇ");
 7614    cx.simulate_keystroke(".");
 7615    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7616    cx.simulate_keystroke("c");
 7617    cx.simulate_keystroke("l");
 7618    cx.simulate_keystroke("o");
 7619    cx.assert_editor_state("editor.cloˇ");
 7620    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7621    cx.update_editor(|editor, cx| {
 7622        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7623    });
 7624    handle_completion_request(
 7625        &mut cx,
 7626        "editor.<clo|>",
 7627        vec!["close", "clobber"],
 7628        counter.clone(),
 7629    )
 7630    .await;
 7631    cx.condition(|editor, _| editor.context_menu_visible())
 7632        .await;
 7633    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7634
 7635    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7636        editor
 7637            .confirm_completion(&ConfirmCompletion::default(), cx)
 7638            .unwrap()
 7639    });
 7640    cx.assert_editor_state("editor.closeˇ");
 7641    handle_resolve_completion_request(&mut cx, None).await;
 7642    apply_additional_edits.await.unwrap();
 7643}
 7644
 7645#[gpui::test]
 7646async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7647    init_test(cx, |_| {});
 7648    let mut cx = EditorLspTestContext::new_rust(
 7649        lsp::ServerCapabilities {
 7650            completion_provider: Some(lsp::CompletionOptions {
 7651                trigger_characters: Some(vec![".".to_string()]),
 7652                ..Default::default()
 7653            }),
 7654            ..Default::default()
 7655        },
 7656        cx,
 7657    )
 7658    .await;
 7659    cx.lsp
 7660        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7661            Ok(Some(lsp::CompletionResponse::Array(vec![
 7662                lsp::CompletionItem {
 7663                    label: "first".into(),
 7664                    ..Default::default()
 7665                },
 7666                lsp::CompletionItem {
 7667                    label: "last".into(),
 7668                    ..Default::default()
 7669                },
 7670            ])))
 7671        });
 7672    cx.set_state("variableˇ");
 7673    cx.simulate_keystroke(".");
 7674    cx.executor().run_until_parked();
 7675
 7676    cx.update_editor(|editor, _| {
 7677        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7678            assert_eq!(
 7679                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7680                &["first", "last"]
 7681            );
 7682        } else {
 7683            panic!("expected completion menu to be open");
 7684        }
 7685    });
 7686
 7687    cx.update_editor(|editor, cx| {
 7688        editor.move_page_down(&MovePageDown::default(), cx);
 7689        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7690            assert!(
 7691                menu.selected_item == 1,
 7692                "expected PageDown to select the last item from the context menu"
 7693            );
 7694        } else {
 7695            panic!("expected completion menu to stay open after PageDown");
 7696        }
 7697    });
 7698
 7699    cx.update_editor(|editor, cx| {
 7700        editor.move_page_up(&MovePageUp::default(), cx);
 7701        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7702            assert!(
 7703                menu.selected_item == 0,
 7704                "expected PageUp to select the first item from the context menu"
 7705            );
 7706        } else {
 7707            panic!("expected completion menu to stay open after PageUp");
 7708        }
 7709    });
 7710}
 7711
 7712#[gpui::test]
 7713async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7714    init_test(cx, |_| {});
 7715
 7716    let mut cx = EditorLspTestContext::new_rust(
 7717        lsp::ServerCapabilities {
 7718            completion_provider: Some(lsp::CompletionOptions {
 7719                trigger_characters: Some(vec![".".to_string()]),
 7720                resolve_provider: Some(true),
 7721                ..Default::default()
 7722            }),
 7723            ..Default::default()
 7724        },
 7725        cx,
 7726    )
 7727    .await;
 7728
 7729    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7730    cx.simulate_keystroke(".");
 7731    let completion_item = lsp::CompletionItem {
 7732        label: "Some".into(),
 7733        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7734        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7735        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7736            kind: lsp::MarkupKind::Markdown,
 7737            value: "```rust\nSome(2)\n```".to_string(),
 7738        })),
 7739        deprecated: Some(false),
 7740        sort_text: Some("Some".to_string()),
 7741        filter_text: Some("Some".to_string()),
 7742        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7743        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7744            range: lsp::Range {
 7745                start: lsp::Position {
 7746                    line: 0,
 7747                    character: 22,
 7748                },
 7749                end: lsp::Position {
 7750                    line: 0,
 7751                    character: 22,
 7752                },
 7753            },
 7754            new_text: "Some(2)".to_string(),
 7755        })),
 7756        additional_text_edits: Some(vec![lsp::TextEdit {
 7757            range: lsp::Range {
 7758                start: lsp::Position {
 7759                    line: 0,
 7760                    character: 20,
 7761                },
 7762                end: lsp::Position {
 7763                    line: 0,
 7764                    character: 22,
 7765                },
 7766            },
 7767            new_text: "".to_string(),
 7768        }]),
 7769        ..Default::default()
 7770    };
 7771
 7772    let closure_completion_item = completion_item.clone();
 7773    let counter = Arc::new(AtomicUsize::new(0));
 7774    let counter_clone = counter.clone();
 7775    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7776        let task_completion_item = closure_completion_item.clone();
 7777        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7778        async move {
 7779            Ok(Some(lsp::CompletionResponse::Array(vec![
 7780                task_completion_item,
 7781            ])))
 7782        }
 7783    });
 7784
 7785    cx.condition(|editor, _| editor.context_menu_visible())
 7786        .await;
 7787    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7788    assert!(request.next().await.is_some());
 7789    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7790
 7791    cx.simulate_keystroke("S");
 7792    cx.simulate_keystroke("o");
 7793    cx.simulate_keystroke("m");
 7794    cx.condition(|editor, _| editor.context_menu_visible())
 7795        .await;
 7796    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7797    assert!(request.next().await.is_some());
 7798    assert!(request.next().await.is_some());
 7799    assert!(request.next().await.is_some());
 7800    request.close();
 7801    assert!(request.next().await.is_none());
 7802    assert_eq!(
 7803        counter.load(atomic::Ordering::Acquire),
 7804        4,
 7805        "With the completions menu open, only one LSP request should happen per input"
 7806    );
 7807}
 7808
 7809#[gpui::test]
 7810async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7811    init_test(cx, |_| {});
 7812    let mut cx = EditorTestContext::new(cx).await;
 7813    let language = Arc::new(Language::new(
 7814        LanguageConfig {
 7815            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7816            ..Default::default()
 7817        },
 7818        Some(tree_sitter_rust::language()),
 7819    ));
 7820    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7821
 7822    // If multiple selections intersect a line, the line is only toggled once.
 7823    cx.set_state(indoc! {"
 7824        fn a() {
 7825            «//b();
 7826            ˇ»// «c();
 7827            //ˇ»  d();
 7828        }
 7829    "});
 7830
 7831    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7832
 7833    cx.assert_editor_state(indoc! {"
 7834        fn a() {
 7835            «b();
 7836            c();
 7837            ˇ» d();
 7838        }
 7839    "});
 7840
 7841    // The comment prefix is inserted at the same column for every line in a
 7842    // selection.
 7843    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7844
 7845    cx.assert_editor_state(indoc! {"
 7846        fn a() {
 7847            // «b();
 7848            // c();
 7849            ˇ»//  d();
 7850        }
 7851    "});
 7852
 7853    // If a selection ends at the beginning of a line, that line is not toggled.
 7854    cx.set_selections_state(indoc! {"
 7855        fn a() {
 7856            // b();
 7857            «// c();
 7858        ˇ»    //  d();
 7859        }
 7860    "});
 7861
 7862    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7863
 7864    cx.assert_editor_state(indoc! {"
 7865        fn a() {
 7866            // b();
 7867            «c();
 7868        ˇ»    //  d();
 7869        }
 7870    "});
 7871
 7872    // If a selection span a single line and is empty, the line is toggled.
 7873    cx.set_state(indoc! {"
 7874        fn a() {
 7875            a();
 7876            b();
 7877        ˇ
 7878        }
 7879    "});
 7880
 7881    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7882
 7883    cx.assert_editor_state(indoc! {"
 7884        fn a() {
 7885            a();
 7886            b();
 7887        //•ˇ
 7888        }
 7889    "});
 7890
 7891    // If a selection span multiple lines, empty lines are not toggled.
 7892    cx.set_state(indoc! {"
 7893        fn a() {
 7894            «a();
 7895
 7896            c();ˇ»
 7897        }
 7898    "});
 7899
 7900    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7901
 7902    cx.assert_editor_state(indoc! {"
 7903        fn a() {
 7904            // «a();
 7905
 7906            // c();ˇ»
 7907        }
 7908    "});
 7909
 7910    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7911    cx.set_state(indoc! {"
 7912        fn a() {
 7913            «// a();
 7914            /// b();
 7915            //! c();ˇ»
 7916        }
 7917    "});
 7918
 7919    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7920
 7921    cx.assert_editor_state(indoc! {"
 7922        fn a() {
 7923            «a();
 7924            b();
 7925            c();ˇ»
 7926        }
 7927    "});
 7928}
 7929
 7930#[gpui::test]
 7931async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7932    init_test(cx, |_| {});
 7933
 7934    let language = Arc::new(Language::new(
 7935        LanguageConfig {
 7936            line_comments: vec!["// ".into()],
 7937            ..Default::default()
 7938        },
 7939        Some(tree_sitter_rust::language()),
 7940    ));
 7941
 7942    let mut cx = EditorTestContext::new(cx).await;
 7943
 7944    cx.language_registry().add(language.clone());
 7945    cx.update_buffer(|buffer, cx| {
 7946        buffer.set_language(Some(language), cx);
 7947    });
 7948
 7949    let toggle_comments = &ToggleComments {
 7950        advance_downwards: true,
 7951    };
 7952
 7953    // Single cursor on one line -> advance
 7954    // Cursor moves horizontally 3 characters as well on non-blank line
 7955    cx.set_state(indoc!(
 7956        "fn a() {
 7957             ˇdog();
 7958             cat();
 7959        }"
 7960    ));
 7961    cx.update_editor(|editor, cx| {
 7962        editor.toggle_comments(toggle_comments, cx);
 7963    });
 7964    cx.assert_editor_state(indoc!(
 7965        "fn a() {
 7966             // dog();
 7967             catˇ();
 7968        }"
 7969    ));
 7970
 7971    // Single selection on one line -> don't advance
 7972    cx.set_state(indoc!(
 7973        "fn a() {
 7974             «dog()ˇ»;
 7975             cat();
 7976        }"
 7977    ));
 7978    cx.update_editor(|editor, cx| {
 7979        editor.toggle_comments(toggle_comments, cx);
 7980    });
 7981    cx.assert_editor_state(indoc!(
 7982        "fn a() {
 7983             // «dog()ˇ»;
 7984             cat();
 7985        }"
 7986    ));
 7987
 7988    // Multiple cursors on one line -> advance
 7989    cx.set_state(indoc!(
 7990        "fn a() {
 7991             ˇdˇog();
 7992             cat();
 7993        }"
 7994    ));
 7995    cx.update_editor(|editor, cx| {
 7996        editor.toggle_comments(toggle_comments, cx);
 7997    });
 7998    cx.assert_editor_state(indoc!(
 7999        "fn a() {
 8000             // dog();
 8001             catˇ(ˇ);
 8002        }"
 8003    ));
 8004
 8005    // Multiple cursors on one line, with selection -> don't advance
 8006    cx.set_state(indoc!(
 8007        "fn a() {
 8008             ˇdˇog«()ˇ»;
 8009             cat();
 8010        }"
 8011    ));
 8012    cx.update_editor(|editor, cx| {
 8013        editor.toggle_comments(toggle_comments, cx);
 8014    });
 8015    cx.assert_editor_state(indoc!(
 8016        "fn a() {
 8017             // ˇdˇog«()ˇ»;
 8018             cat();
 8019        }"
 8020    ));
 8021
 8022    // Single cursor on one line -> advance
 8023    // Cursor moves to column 0 on blank line
 8024    cx.set_state(indoc!(
 8025        "fn a() {
 8026             ˇdog();
 8027
 8028             cat();
 8029        }"
 8030    ));
 8031    cx.update_editor(|editor, cx| {
 8032        editor.toggle_comments(toggle_comments, cx);
 8033    });
 8034    cx.assert_editor_state(indoc!(
 8035        "fn a() {
 8036             // dog();
 8037        ˇ
 8038             cat();
 8039        }"
 8040    ));
 8041
 8042    // Single cursor on one line -> advance
 8043    // Cursor starts and ends at column 0
 8044    cx.set_state(indoc!(
 8045        "fn a() {
 8046         ˇ    dog();
 8047             cat();
 8048        }"
 8049    ));
 8050    cx.update_editor(|editor, cx| {
 8051        editor.toggle_comments(toggle_comments, cx);
 8052    });
 8053    cx.assert_editor_state(indoc!(
 8054        "fn a() {
 8055             // dog();
 8056         ˇ    cat();
 8057        }"
 8058    ));
 8059}
 8060
 8061#[gpui::test]
 8062async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8063    init_test(cx, |_| {});
 8064
 8065    let mut cx = EditorTestContext::new(cx).await;
 8066
 8067    let html_language = Arc::new(
 8068        Language::new(
 8069            LanguageConfig {
 8070                name: "HTML".into(),
 8071                block_comment: Some(("<!-- ".into(), " -->".into())),
 8072                ..Default::default()
 8073            },
 8074            Some(tree_sitter_html::language()),
 8075        )
 8076        .with_injection_query(
 8077            r#"
 8078            (script_element
 8079                (raw_text) @content
 8080                (#set! "language" "javascript"))
 8081            "#,
 8082        )
 8083        .unwrap(),
 8084    );
 8085
 8086    let javascript_language = Arc::new(Language::new(
 8087        LanguageConfig {
 8088            name: "JavaScript".into(),
 8089            line_comments: vec!["// ".into()],
 8090            ..Default::default()
 8091        },
 8092        Some(tree_sitter_typescript::language_tsx()),
 8093    ));
 8094
 8095    cx.language_registry().add(html_language.clone());
 8096    cx.language_registry().add(javascript_language.clone());
 8097    cx.update_buffer(|buffer, cx| {
 8098        buffer.set_language(Some(html_language), cx);
 8099    });
 8100
 8101    // Toggle comments for empty selections
 8102    cx.set_state(
 8103        &r#"
 8104            <p>A</p>ˇ
 8105            <p>B</p>ˇ
 8106            <p>C</p>ˇ
 8107        "#
 8108        .unindent(),
 8109    );
 8110    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8111    cx.assert_editor_state(
 8112        &r#"
 8113            <!-- <p>A</p>ˇ -->
 8114            <!-- <p>B</p>ˇ -->
 8115            <!-- <p>C</p>ˇ -->
 8116        "#
 8117        .unindent(),
 8118    );
 8119    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8120    cx.assert_editor_state(
 8121        &r#"
 8122            <p>A</p>ˇ
 8123            <p>B</p>ˇ
 8124            <p>C</p>ˇ
 8125        "#
 8126        .unindent(),
 8127    );
 8128
 8129    // Toggle comments for mixture of empty and non-empty selections, where
 8130    // multiple selections occupy a given line.
 8131    cx.set_state(
 8132        &r#"
 8133            <p>A«</p>
 8134            <p>ˇ»B</p>ˇ
 8135            <p>C«</p>
 8136            <p>ˇ»D</p>ˇ
 8137        "#
 8138        .unindent(),
 8139    );
 8140
 8141    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8142    cx.assert_editor_state(
 8143        &r#"
 8144            <!-- <p>A«</p>
 8145            <p>ˇ»B</p>ˇ -->
 8146            <!-- <p>C«</p>
 8147            <p>ˇ»D</p>ˇ -->
 8148        "#
 8149        .unindent(),
 8150    );
 8151    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8152    cx.assert_editor_state(
 8153        &r#"
 8154            <p>A«</p>
 8155            <p>ˇ»B</p>ˇ
 8156            <p>C«</p>
 8157            <p>ˇ»D</p>ˇ
 8158        "#
 8159        .unindent(),
 8160    );
 8161
 8162    // Toggle comments when different languages are active for different
 8163    // selections.
 8164    cx.set_state(
 8165        &r#"
 8166            ˇ<script>
 8167                ˇvar x = new Y();
 8168            ˇ</script>
 8169        "#
 8170        .unindent(),
 8171    );
 8172    cx.executor().run_until_parked();
 8173    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8174    cx.assert_editor_state(
 8175        &r#"
 8176            <!-- ˇ<script> -->
 8177                // ˇvar x = new Y();
 8178            <!-- ˇ</script> -->
 8179        "#
 8180        .unindent(),
 8181    );
 8182}
 8183
 8184#[gpui::test]
 8185fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8186    init_test(cx, |_| {});
 8187
 8188    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8189    let multibuffer = cx.new_model(|cx| {
 8190        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8191        multibuffer.push_excerpts(
 8192            buffer.clone(),
 8193            [
 8194                ExcerptRange {
 8195                    context: Point::new(0, 0)..Point::new(0, 4),
 8196                    primary: None,
 8197                },
 8198                ExcerptRange {
 8199                    context: Point::new(1, 0)..Point::new(1, 4),
 8200                    primary: None,
 8201                },
 8202            ],
 8203            cx,
 8204        );
 8205        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8206        multibuffer
 8207    });
 8208
 8209    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8210    _ = view.update(cx, |view, cx| {
 8211        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8212        view.change_selections(None, cx, |s| {
 8213            s.select_ranges([
 8214                Point::new(0, 0)..Point::new(0, 0),
 8215                Point::new(1, 0)..Point::new(1, 0),
 8216            ])
 8217        });
 8218
 8219        view.handle_input("X", cx);
 8220        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8221        assert_eq!(
 8222            view.selections.ranges(cx),
 8223            [
 8224                Point::new(0, 1)..Point::new(0, 1),
 8225                Point::new(1, 1)..Point::new(1, 1),
 8226            ]
 8227        );
 8228
 8229        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8230        view.change_selections(None, cx, |s| {
 8231            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8232        });
 8233        view.backspace(&Default::default(), cx);
 8234        assert_eq!(view.text(cx), "Xa\nbbb");
 8235        assert_eq!(
 8236            view.selections.ranges(cx),
 8237            [Point::new(1, 0)..Point::new(1, 0)]
 8238        );
 8239
 8240        view.change_selections(None, cx, |s| {
 8241            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8242        });
 8243        view.backspace(&Default::default(), cx);
 8244        assert_eq!(view.text(cx), "X\nbb");
 8245        assert_eq!(
 8246            view.selections.ranges(cx),
 8247            [Point::new(0, 1)..Point::new(0, 1)]
 8248        );
 8249    });
 8250}
 8251
 8252#[gpui::test]
 8253fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8254    init_test(cx, |_| {});
 8255
 8256    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8257    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8258        indoc! {"
 8259            [aaaa
 8260            (bbbb]
 8261            cccc)",
 8262        },
 8263        markers.clone(),
 8264    );
 8265    let excerpt_ranges = markers.into_iter().map(|marker| {
 8266        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8267        ExcerptRange {
 8268            context,
 8269            primary: None,
 8270        }
 8271    });
 8272    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8273    let multibuffer = cx.new_model(|cx| {
 8274        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8275        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8276        multibuffer
 8277    });
 8278
 8279    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8280    _ = view.update(cx, |view, cx| {
 8281        let (expected_text, selection_ranges) = marked_text_ranges(
 8282            indoc! {"
 8283                aaaa
 8284                bˇbbb
 8285                bˇbbˇb
 8286                cccc"
 8287            },
 8288            true,
 8289        );
 8290        assert_eq!(view.text(cx), expected_text);
 8291        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8292
 8293        view.handle_input("X", cx);
 8294
 8295        let (expected_text, expected_selections) = marked_text_ranges(
 8296            indoc! {"
 8297                aaaa
 8298                bXˇbbXb
 8299                bXˇbbXˇb
 8300                cccc"
 8301            },
 8302            false,
 8303        );
 8304        assert_eq!(view.text(cx), expected_text);
 8305        assert_eq!(view.selections.ranges(cx), expected_selections);
 8306
 8307        view.newline(&Newline, cx);
 8308        let (expected_text, expected_selections) = marked_text_ranges(
 8309            indoc! {"
 8310                aaaa
 8311                bX
 8312                ˇbbX
 8313                b
 8314                bX
 8315                ˇbbX
 8316                ˇb
 8317                cccc"
 8318            },
 8319            false,
 8320        );
 8321        assert_eq!(view.text(cx), expected_text);
 8322        assert_eq!(view.selections.ranges(cx), expected_selections);
 8323    });
 8324}
 8325
 8326#[gpui::test]
 8327fn test_refresh_selections(cx: &mut TestAppContext) {
 8328    init_test(cx, |_| {});
 8329
 8330    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8331    let mut excerpt1_id = None;
 8332    let multibuffer = cx.new_model(|cx| {
 8333        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8334        excerpt1_id = multibuffer
 8335            .push_excerpts(
 8336                buffer.clone(),
 8337                [
 8338                    ExcerptRange {
 8339                        context: Point::new(0, 0)..Point::new(1, 4),
 8340                        primary: None,
 8341                    },
 8342                    ExcerptRange {
 8343                        context: Point::new(1, 0)..Point::new(2, 4),
 8344                        primary: None,
 8345                    },
 8346                ],
 8347                cx,
 8348            )
 8349            .into_iter()
 8350            .next();
 8351        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8352        multibuffer
 8353    });
 8354
 8355    let editor = cx.add_window(|cx| {
 8356        let mut editor = build_editor(multibuffer.clone(), cx);
 8357        let snapshot = editor.snapshot(cx);
 8358        editor.change_selections(None, cx, |s| {
 8359            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8360        });
 8361        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8362        assert_eq!(
 8363            editor.selections.ranges(cx),
 8364            [
 8365                Point::new(1, 3)..Point::new(1, 3),
 8366                Point::new(2, 1)..Point::new(2, 1),
 8367            ]
 8368        );
 8369        editor
 8370    });
 8371
 8372    // Refreshing selections is a no-op when excerpts haven't changed.
 8373    _ = editor.update(cx, |editor, cx| {
 8374        editor.change_selections(None, cx, |s| s.refresh());
 8375        assert_eq!(
 8376            editor.selections.ranges(cx),
 8377            [
 8378                Point::new(1, 3)..Point::new(1, 3),
 8379                Point::new(2, 1)..Point::new(2, 1),
 8380            ]
 8381        );
 8382    });
 8383
 8384    _ = multibuffer.update(cx, |multibuffer, cx| {
 8385        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8386    });
 8387    _ = editor.update(cx, |editor, cx| {
 8388        // Removing an excerpt causes the first selection to become degenerate.
 8389        assert_eq!(
 8390            editor.selections.ranges(cx),
 8391            [
 8392                Point::new(0, 0)..Point::new(0, 0),
 8393                Point::new(0, 1)..Point::new(0, 1)
 8394            ]
 8395        );
 8396
 8397        // Refreshing selections will relocate the first selection to the original buffer
 8398        // location.
 8399        editor.change_selections(None, cx, |s| s.refresh());
 8400        assert_eq!(
 8401            editor.selections.ranges(cx),
 8402            [
 8403                Point::new(0, 1)..Point::new(0, 1),
 8404                Point::new(0, 3)..Point::new(0, 3)
 8405            ]
 8406        );
 8407        assert!(editor.selections.pending_anchor().is_some());
 8408    });
 8409}
 8410
 8411#[gpui::test]
 8412fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8413    init_test(cx, |_| {});
 8414
 8415    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8416    let mut excerpt1_id = None;
 8417    let multibuffer = cx.new_model(|cx| {
 8418        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8419        excerpt1_id = multibuffer
 8420            .push_excerpts(
 8421                buffer.clone(),
 8422                [
 8423                    ExcerptRange {
 8424                        context: Point::new(0, 0)..Point::new(1, 4),
 8425                        primary: None,
 8426                    },
 8427                    ExcerptRange {
 8428                        context: Point::new(1, 0)..Point::new(2, 4),
 8429                        primary: None,
 8430                    },
 8431                ],
 8432                cx,
 8433            )
 8434            .into_iter()
 8435            .next();
 8436        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8437        multibuffer
 8438    });
 8439
 8440    let editor = cx.add_window(|cx| {
 8441        let mut editor = build_editor(multibuffer.clone(), cx);
 8442        let snapshot = editor.snapshot(cx);
 8443        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8444        assert_eq!(
 8445            editor.selections.ranges(cx),
 8446            [Point::new(1, 3)..Point::new(1, 3)]
 8447        );
 8448        editor
 8449    });
 8450
 8451    _ = multibuffer.update(cx, |multibuffer, cx| {
 8452        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8453    });
 8454    _ = editor.update(cx, |editor, cx| {
 8455        assert_eq!(
 8456            editor.selections.ranges(cx),
 8457            [Point::new(0, 0)..Point::new(0, 0)]
 8458        );
 8459
 8460        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8461        editor.change_selections(None, cx, |s| s.refresh());
 8462        assert_eq!(
 8463            editor.selections.ranges(cx),
 8464            [Point::new(0, 3)..Point::new(0, 3)]
 8465        );
 8466        assert!(editor.selections.pending_anchor().is_some());
 8467    });
 8468}
 8469
 8470#[gpui::test]
 8471async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8472    init_test(cx, |_| {});
 8473
 8474    let language = Arc::new(
 8475        Language::new(
 8476            LanguageConfig {
 8477                brackets: BracketPairConfig {
 8478                    pairs: vec![
 8479                        BracketPair {
 8480                            start: "{".to_string(),
 8481                            end: "}".to_string(),
 8482                            close: true,
 8483                            surround: true,
 8484                            newline: true,
 8485                        },
 8486                        BracketPair {
 8487                            start: "/* ".to_string(),
 8488                            end: " */".to_string(),
 8489                            close: true,
 8490                            surround: true,
 8491                            newline: true,
 8492                        },
 8493                    ],
 8494                    ..Default::default()
 8495                },
 8496                ..Default::default()
 8497            },
 8498            Some(tree_sitter_rust::language()),
 8499        )
 8500        .with_indents_query("")
 8501        .unwrap(),
 8502    );
 8503
 8504    let text = concat!(
 8505        "{   }\n",     //
 8506        "  x\n",       //
 8507        "  /*   */\n", //
 8508        "x\n",         //
 8509        "{{} }\n",     //
 8510    );
 8511
 8512    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8513    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8514    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8515    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8516        .await;
 8517
 8518    _ = view.update(cx, |view, cx| {
 8519        view.change_selections(None, cx, |s| {
 8520            s.select_display_ranges([
 8521                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8522                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8523                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8524            ])
 8525        });
 8526        view.newline(&Newline, cx);
 8527
 8528        assert_eq!(
 8529            view.buffer().read(cx).read(cx).text(),
 8530            concat!(
 8531                "{ \n",    // Suppress rustfmt
 8532                "\n",      //
 8533                "}\n",     //
 8534                "  x\n",   //
 8535                "  /* \n", //
 8536                "  \n",    //
 8537                "  */\n",  //
 8538                "x\n",     //
 8539                "{{} \n",  //
 8540                "}\n",     //
 8541            )
 8542        );
 8543    });
 8544}
 8545
 8546#[gpui::test]
 8547fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8548    init_test(cx, |_| {});
 8549
 8550    let editor = cx.add_window(|cx| {
 8551        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8552        build_editor(buffer.clone(), cx)
 8553    });
 8554
 8555    _ = editor.update(cx, |editor, cx| {
 8556        struct Type1;
 8557        struct Type2;
 8558
 8559        let buffer = editor.buffer.read(cx).snapshot(cx);
 8560
 8561        let anchor_range =
 8562            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8563
 8564        editor.highlight_background::<Type1>(
 8565            &[
 8566                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8567                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8568                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8569                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8570            ],
 8571            |_| Hsla::red(),
 8572            cx,
 8573        );
 8574        editor.highlight_background::<Type2>(
 8575            &[
 8576                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8577                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8578                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8579                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8580            ],
 8581            |_| Hsla::green(),
 8582            cx,
 8583        );
 8584
 8585        let snapshot = editor.snapshot(cx);
 8586        let mut highlighted_ranges = editor.background_highlights_in_range(
 8587            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8588            &snapshot,
 8589            cx.theme().colors(),
 8590        );
 8591        // Enforce a consistent ordering based on color without relying on the ordering of the
 8592        // highlight's `TypeId` which is non-executor.
 8593        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8594        assert_eq!(
 8595            highlighted_ranges,
 8596            &[
 8597                (
 8598                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8599                    Hsla::red(),
 8600                ),
 8601                (
 8602                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8603                    Hsla::red(),
 8604                ),
 8605                (
 8606                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8607                    Hsla::green(),
 8608                ),
 8609                (
 8610                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8611                    Hsla::green(),
 8612                ),
 8613            ]
 8614        );
 8615        assert_eq!(
 8616            editor.background_highlights_in_range(
 8617                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8618                &snapshot,
 8619                cx.theme().colors(),
 8620            ),
 8621            &[(
 8622                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8623                Hsla::red(),
 8624            )]
 8625        );
 8626    });
 8627}
 8628
 8629#[gpui::test]
 8630async fn test_following(cx: &mut gpui::TestAppContext) {
 8631    init_test(cx, |_| {});
 8632
 8633    let fs = FakeFs::new(cx.executor());
 8634    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8635
 8636    let buffer = project.update(cx, |project, cx| {
 8637        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8638        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8639    });
 8640    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8641    let follower = cx.update(|cx| {
 8642        cx.open_window(
 8643            WindowOptions {
 8644                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8645                    gpui::Point::new(px(0.), px(0.)),
 8646                    gpui::Point::new(px(10.), px(80.)),
 8647                ))),
 8648                ..Default::default()
 8649            },
 8650            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8651        )
 8652        .unwrap()
 8653    });
 8654
 8655    let is_still_following = Rc::new(RefCell::new(true));
 8656    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8657    let pending_update = Rc::new(RefCell::new(None));
 8658    _ = follower.update(cx, {
 8659        let update = pending_update.clone();
 8660        let is_still_following = is_still_following.clone();
 8661        let follower_edit_event_count = follower_edit_event_count.clone();
 8662        |_, cx| {
 8663            cx.subscribe(
 8664                &leader.root_view(cx).unwrap(),
 8665                move |_, leader, event, cx| {
 8666                    leader
 8667                        .read(cx)
 8668                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8669                },
 8670            )
 8671            .detach();
 8672
 8673            cx.subscribe(
 8674                &follower.root_view(cx).unwrap(),
 8675                move |_, _, event: &EditorEvent, _cx| {
 8676                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8677                        *is_still_following.borrow_mut() = false;
 8678                    }
 8679
 8680                    if let EditorEvent::BufferEdited = event {
 8681                        *follower_edit_event_count.borrow_mut() += 1;
 8682                    }
 8683                },
 8684            )
 8685            .detach();
 8686        }
 8687    });
 8688
 8689    // Update the selections only
 8690    _ = leader.update(cx, |leader, cx| {
 8691        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8692    });
 8693    follower
 8694        .update(cx, |follower, cx| {
 8695            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8696        })
 8697        .unwrap()
 8698        .await
 8699        .unwrap();
 8700    _ = follower.update(cx, |follower, cx| {
 8701        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8702    });
 8703    assert_eq!(*is_still_following.borrow(), true);
 8704    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8705
 8706    // Update the scroll position only
 8707    _ = leader.update(cx, |leader, cx| {
 8708        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8709    });
 8710    follower
 8711        .update(cx, |follower, cx| {
 8712            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8713        })
 8714        .unwrap()
 8715        .await
 8716        .unwrap();
 8717    assert_eq!(
 8718        follower
 8719            .update(cx, |follower, cx| follower.scroll_position(cx))
 8720            .unwrap(),
 8721        gpui::Point::new(1.5, 3.5)
 8722    );
 8723    assert_eq!(*is_still_following.borrow(), true);
 8724    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8725
 8726    // Update the selections and scroll position. The follower's scroll position is updated
 8727    // via autoscroll, not via the leader's exact scroll position.
 8728    _ = leader.update(cx, |leader, cx| {
 8729        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8730        leader.request_autoscroll(Autoscroll::newest(), cx);
 8731        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8732    });
 8733    follower
 8734        .update(cx, |follower, cx| {
 8735            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8736        })
 8737        .unwrap()
 8738        .await
 8739        .unwrap();
 8740    _ = follower.update(cx, |follower, cx| {
 8741        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8742        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8743    });
 8744    assert_eq!(*is_still_following.borrow(), true);
 8745
 8746    // Creating a pending selection that precedes another selection
 8747    _ = leader.update(cx, |leader, cx| {
 8748        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8749        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8750    });
 8751    follower
 8752        .update(cx, |follower, cx| {
 8753            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8754        })
 8755        .unwrap()
 8756        .await
 8757        .unwrap();
 8758    _ = follower.update(cx, |follower, cx| {
 8759        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8760    });
 8761    assert_eq!(*is_still_following.borrow(), true);
 8762
 8763    // Extend the pending selection so that it surrounds another selection
 8764    _ = leader.update(cx, |leader, cx| {
 8765        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8766    });
 8767    follower
 8768        .update(cx, |follower, cx| {
 8769            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8770        })
 8771        .unwrap()
 8772        .await
 8773        .unwrap();
 8774    _ = follower.update(cx, |follower, cx| {
 8775        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8776    });
 8777
 8778    // Scrolling locally breaks the follow
 8779    _ = follower.update(cx, |follower, cx| {
 8780        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8781        follower.set_scroll_anchor(
 8782            ScrollAnchor {
 8783                anchor: top_anchor,
 8784                offset: gpui::Point::new(0.0, 0.5),
 8785            },
 8786            cx,
 8787        );
 8788    });
 8789    assert_eq!(*is_still_following.borrow(), false);
 8790}
 8791
 8792#[gpui::test]
 8793async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8794    init_test(cx, |_| {});
 8795
 8796    let fs = FakeFs::new(cx.executor());
 8797    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8798    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8799    let pane = workspace
 8800        .update(cx, |workspace, _| workspace.active_pane().clone())
 8801        .unwrap();
 8802
 8803    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8804
 8805    let leader = pane.update(cx, |_, cx| {
 8806        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8807        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8808    });
 8809
 8810    // Start following the editor when it has no excerpts.
 8811    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8812    let follower_1 = cx
 8813        .update_window(*workspace.deref(), |_, cx| {
 8814            Editor::from_state_proto(
 8815                workspace.root_view(cx).unwrap(),
 8816                ViewId {
 8817                    creator: Default::default(),
 8818                    id: 0,
 8819                },
 8820                &mut state_message,
 8821                cx,
 8822            )
 8823        })
 8824        .unwrap()
 8825        .unwrap()
 8826        .await
 8827        .unwrap();
 8828
 8829    let update_message = Rc::new(RefCell::new(None));
 8830    follower_1.update(cx, {
 8831        let update = update_message.clone();
 8832        |_, cx| {
 8833            cx.subscribe(&leader, move |_, leader, event, cx| {
 8834                leader
 8835                    .read(cx)
 8836                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8837            })
 8838            .detach();
 8839        }
 8840    });
 8841
 8842    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8843        (
 8844            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8845            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8846        )
 8847    });
 8848
 8849    // Insert some excerpts.
 8850    _ = leader.update(cx, |leader, cx| {
 8851        leader.buffer.update(cx, |multibuffer, cx| {
 8852            let excerpt_ids = multibuffer.push_excerpts(
 8853                buffer_1.clone(),
 8854                [
 8855                    ExcerptRange {
 8856                        context: 1..6,
 8857                        primary: None,
 8858                    },
 8859                    ExcerptRange {
 8860                        context: 12..15,
 8861                        primary: None,
 8862                    },
 8863                    ExcerptRange {
 8864                        context: 0..3,
 8865                        primary: None,
 8866                    },
 8867                ],
 8868                cx,
 8869            );
 8870            multibuffer.insert_excerpts_after(
 8871                excerpt_ids[0],
 8872                buffer_2.clone(),
 8873                [
 8874                    ExcerptRange {
 8875                        context: 8..12,
 8876                        primary: None,
 8877                    },
 8878                    ExcerptRange {
 8879                        context: 0..6,
 8880                        primary: None,
 8881                    },
 8882                ],
 8883                cx,
 8884            );
 8885        });
 8886    });
 8887
 8888    // Apply the update of adding the excerpts.
 8889    follower_1
 8890        .update(cx, |follower, cx| {
 8891            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8892        })
 8893        .await
 8894        .unwrap();
 8895    assert_eq!(
 8896        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8897        leader.update(cx, |editor, cx| editor.text(cx))
 8898    );
 8899    update_message.borrow_mut().take();
 8900
 8901    // Start following separately after it already has excerpts.
 8902    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8903    let follower_2 = cx
 8904        .update_window(*workspace.deref(), |_, cx| {
 8905            Editor::from_state_proto(
 8906                workspace.root_view(cx).unwrap().clone(),
 8907                ViewId {
 8908                    creator: Default::default(),
 8909                    id: 0,
 8910                },
 8911                &mut state_message,
 8912                cx,
 8913            )
 8914        })
 8915        .unwrap()
 8916        .unwrap()
 8917        .await
 8918        .unwrap();
 8919    assert_eq!(
 8920        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8921        leader.update(cx, |editor, cx| editor.text(cx))
 8922    );
 8923
 8924    // Remove some excerpts.
 8925    _ = leader.update(cx, |leader, cx| {
 8926        leader.buffer.update(cx, |multibuffer, cx| {
 8927            let excerpt_ids = multibuffer.excerpt_ids();
 8928            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8929            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8930        });
 8931    });
 8932
 8933    // Apply the update of removing the excerpts.
 8934    follower_1
 8935        .update(cx, |follower, cx| {
 8936            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8937        })
 8938        .await
 8939        .unwrap();
 8940    follower_2
 8941        .update(cx, |follower, cx| {
 8942            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8943        })
 8944        .await
 8945        .unwrap();
 8946    update_message.borrow_mut().take();
 8947    assert_eq!(
 8948        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8949        leader.update(cx, |editor, cx| editor.text(cx))
 8950    );
 8951}
 8952
 8953#[gpui::test]
 8954async fn go_to_prev_overlapping_diagnostic(
 8955    executor: BackgroundExecutor,
 8956    cx: &mut gpui::TestAppContext,
 8957) {
 8958    init_test(cx, |_| {});
 8959
 8960    let mut cx = EditorTestContext::new(cx).await;
 8961    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 8962
 8963    cx.set_state(indoc! {"
 8964        ˇfn func(abc def: i32) -> u32 {
 8965        }
 8966    "});
 8967
 8968    _ = cx.update(|cx| {
 8969        _ = project.update(cx, |project, cx| {
 8970            project
 8971                .update_diagnostics(
 8972                    LanguageServerId(0),
 8973                    lsp::PublishDiagnosticsParams {
 8974                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 8975                        version: None,
 8976                        diagnostics: vec![
 8977                            lsp::Diagnostic {
 8978                                range: lsp::Range::new(
 8979                                    lsp::Position::new(0, 11),
 8980                                    lsp::Position::new(0, 12),
 8981                                ),
 8982                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8983                                ..Default::default()
 8984                            },
 8985                            lsp::Diagnostic {
 8986                                range: lsp::Range::new(
 8987                                    lsp::Position::new(0, 12),
 8988                                    lsp::Position::new(0, 15),
 8989                                ),
 8990                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8991                                ..Default::default()
 8992                            },
 8993                            lsp::Diagnostic {
 8994                                range: lsp::Range::new(
 8995                                    lsp::Position::new(0, 25),
 8996                                    lsp::Position::new(0, 28),
 8997                                ),
 8998                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8999                                ..Default::default()
 9000                            },
 9001                        ],
 9002                    },
 9003                    &[],
 9004                    cx,
 9005                )
 9006                .unwrap()
 9007        });
 9008    });
 9009
 9010    executor.run_until_parked();
 9011
 9012    cx.update_editor(|editor, cx| {
 9013        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9014    });
 9015
 9016    cx.assert_editor_state(indoc! {"
 9017        fn func(abc def: i32) -> ˇu32 {
 9018        }
 9019    "});
 9020
 9021    cx.update_editor(|editor, cx| {
 9022        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9023    });
 9024
 9025    cx.assert_editor_state(indoc! {"
 9026        fn func(abc ˇdef: i32) -> u32 {
 9027        }
 9028    "});
 9029
 9030    cx.update_editor(|editor, cx| {
 9031        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9032    });
 9033
 9034    cx.assert_editor_state(indoc! {"
 9035        fn func(abcˇ def: i32) -> u32 {
 9036        }
 9037    "});
 9038
 9039    cx.update_editor(|editor, cx| {
 9040        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9041    });
 9042
 9043    cx.assert_editor_state(indoc! {"
 9044        fn func(abc def: i32) -> ˇu32 {
 9045        }
 9046    "});
 9047}
 9048
 9049#[gpui::test]
 9050async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9051    init_test(cx, |_| {});
 9052
 9053    let mut cx = EditorTestContext::new(cx).await;
 9054
 9055    let diff_base = r#"
 9056        use some::mod;
 9057
 9058        const A: u32 = 42;
 9059
 9060        fn main() {
 9061            println!("hello");
 9062
 9063            println!("world");
 9064        }
 9065        "#
 9066    .unindent();
 9067
 9068    // Edits are modified, removed, modified, added
 9069    cx.set_state(
 9070        &r#"
 9071        use some::modified;
 9072
 9073        ˇ
 9074        fn main() {
 9075            println!("hello there");
 9076
 9077            println!("around the");
 9078            println!("world");
 9079        }
 9080        "#
 9081        .unindent(),
 9082    );
 9083
 9084    cx.set_diff_base(Some(&diff_base));
 9085    executor.run_until_parked();
 9086
 9087    cx.update_editor(|editor, cx| {
 9088        //Wrap around the bottom of the buffer
 9089        for _ in 0..3 {
 9090            editor.go_to_hunk(&GoToHunk, cx);
 9091        }
 9092    });
 9093
 9094    cx.assert_editor_state(
 9095        &r#"
 9096        ˇuse some::modified;
 9097
 9098
 9099        fn main() {
 9100            println!("hello there");
 9101
 9102            println!("around the");
 9103            println!("world");
 9104        }
 9105        "#
 9106        .unindent(),
 9107    );
 9108
 9109    cx.update_editor(|editor, cx| {
 9110        //Wrap around the top of the buffer
 9111        for _ in 0..2 {
 9112            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9113        }
 9114    });
 9115
 9116    cx.assert_editor_state(
 9117        &r#"
 9118        use some::modified;
 9119
 9120
 9121        fn main() {
 9122        ˇ    println!("hello there");
 9123
 9124            println!("around the");
 9125            println!("world");
 9126        }
 9127        "#
 9128        .unindent(),
 9129    );
 9130
 9131    cx.update_editor(|editor, cx| {
 9132        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9133    });
 9134
 9135    cx.assert_editor_state(
 9136        &r#"
 9137        use some::modified;
 9138
 9139        ˇ
 9140        fn main() {
 9141            println!("hello there");
 9142
 9143            println!("around the");
 9144            println!("world");
 9145        }
 9146        "#
 9147        .unindent(),
 9148    );
 9149
 9150    cx.update_editor(|editor, cx| {
 9151        for _ in 0..3 {
 9152            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9153        }
 9154    });
 9155
 9156    cx.assert_editor_state(
 9157        &r#"
 9158        use some::modified;
 9159
 9160
 9161        fn main() {
 9162        ˇ    println!("hello there");
 9163
 9164            println!("around the");
 9165            println!("world");
 9166        }
 9167        "#
 9168        .unindent(),
 9169    );
 9170
 9171    cx.update_editor(|editor, cx| {
 9172        editor.fold(&Fold, cx);
 9173
 9174        //Make sure that the fold only gets one hunk
 9175        for _ in 0..4 {
 9176            editor.go_to_hunk(&GoToHunk, cx);
 9177        }
 9178    });
 9179
 9180    cx.assert_editor_state(
 9181        &r#"
 9182        ˇuse some::modified;
 9183
 9184
 9185        fn main() {
 9186            println!("hello there");
 9187
 9188            println!("around the");
 9189            println!("world");
 9190        }
 9191        "#
 9192        .unindent(),
 9193    );
 9194}
 9195
 9196#[test]
 9197fn test_split_words() {
 9198    fn split(text: &str) -> Vec<&str> {
 9199        split_words(text).collect()
 9200    }
 9201
 9202    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9203    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9204    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9205    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9206    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9207    assert_eq!(split("helloworld"), &["helloworld"]);
 9208
 9209    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9210}
 9211
 9212#[gpui::test]
 9213async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9214    init_test(cx, |_| {});
 9215
 9216    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9217    let mut assert = |before, after| {
 9218        let _state_context = cx.set_state(before);
 9219        cx.update_editor(|editor, cx| {
 9220            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9221        });
 9222        cx.assert_editor_state(after);
 9223    };
 9224
 9225    // Outside bracket jumps to outside of matching bracket
 9226    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9227    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9228
 9229    // Inside bracket jumps to inside of matching bracket
 9230    assert("console.log(ˇvar);", "console.log(varˇ);");
 9231    assert("console.log(varˇ);", "console.log(ˇvar);");
 9232
 9233    // When outside a bracket and inside, favor jumping to the inside bracket
 9234    assert(
 9235        "console.log('foo', [1, 2, 3]ˇ);",
 9236        "console.log(ˇ'foo', [1, 2, 3]);",
 9237    );
 9238    assert(
 9239        "console.log(ˇ'foo', [1, 2, 3]);",
 9240        "console.log('foo', [1, 2, 3]ˇ);",
 9241    );
 9242
 9243    // Bias forward if two options are equally likely
 9244    assert(
 9245        "let result = curried_fun()ˇ();",
 9246        "let result = curried_fun()()ˇ;",
 9247    );
 9248
 9249    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9250    assert(
 9251        indoc! {"
 9252            function test() {
 9253                console.log('test')ˇ
 9254            }"},
 9255        indoc! {"
 9256            function test() {
 9257                console.logˇ('test')
 9258            }"},
 9259    );
 9260}
 9261
 9262#[gpui::test]
 9263async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9264    init_test(cx, |_| {});
 9265
 9266    let fs = FakeFs::new(cx.executor());
 9267    fs.insert_tree(
 9268        "/a",
 9269        json!({
 9270            "main.rs": "fn main() { let a = 5; }",
 9271            "other.rs": "// Test file",
 9272        }),
 9273    )
 9274    .await;
 9275    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9276
 9277    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9278    language_registry.add(Arc::new(Language::new(
 9279        LanguageConfig {
 9280            name: "Rust".into(),
 9281            matcher: LanguageMatcher {
 9282                path_suffixes: vec!["rs".to_string()],
 9283                ..Default::default()
 9284            },
 9285            brackets: BracketPairConfig {
 9286                pairs: vec![BracketPair {
 9287                    start: "{".to_string(),
 9288                    end: "}".to_string(),
 9289                    close: true,
 9290                    surround: true,
 9291                    newline: true,
 9292                }],
 9293                disabled_scopes_by_bracket_ix: Vec::new(),
 9294            },
 9295            ..Default::default()
 9296        },
 9297        Some(tree_sitter_rust::language()),
 9298    )));
 9299    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9300        "Rust",
 9301        FakeLspAdapter {
 9302            capabilities: lsp::ServerCapabilities {
 9303                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9304                    first_trigger_character: "{".to_string(),
 9305                    more_trigger_character: None,
 9306                }),
 9307                ..Default::default()
 9308            },
 9309            ..Default::default()
 9310        },
 9311    );
 9312
 9313    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9314
 9315    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9316
 9317    let worktree_id = workspace
 9318        .update(cx, |workspace, cx| {
 9319            workspace.project().update(cx, |project, cx| {
 9320                project.worktrees().next().unwrap().read(cx).id()
 9321            })
 9322        })
 9323        .unwrap();
 9324
 9325    let buffer = project
 9326        .update(cx, |project, cx| {
 9327            project.open_local_buffer("/a/main.rs", cx)
 9328        })
 9329        .await
 9330        .unwrap();
 9331    cx.executor().run_until_parked();
 9332    cx.executor().start_waiting();
 9333    let fake_server = fake_servers.next().await.unwrap();
 9334    let editor_handle = workspace
 9335        .update(cx, |workspace, cx| {
 9336            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9337        })
 9338        .unwrap()
 9339        .await
 9340        .unwrap()
 9341        .downcast::<Editor>()
 9342        .unwrap();
 9343
 9344    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9345        assert_eq!(
 9346            params.text_document_position.text_document.uri,
 9347            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9348        );
 9349        assert_eq!(
 9350            params.text_document_position.position,
 9351            lsp::Position::new(0, 21),
 9352        );
 9353
 9354        Ok(Some(vec![lsp::TextEdit {
 9355            new_text: "]".to_string(),
 9356            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9357        }]))
 9358    });
 9359
 9360    editor_handle.update(cx, |editor, cx| {
 9361        editor.focus(cx);
 9362        editor.change_selections(None, cx, |s| {
 9363            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9364        });
 9365        editor.handle_input("{", cx);
 9366    });
 9367
 9368    cx.executor().run_until_parked();
 9369
 9370    _ = buffer.update(cx, |buffer, _| {
 9371        assert_eq!(
 9372            buffer.text(),
 9373            "fn main() { let a = {5}; }",
 9374            "No extra braces from on type formatting should appear in the buffer"
 9375        )
 9376    });
 9377}
 9378
 9379#[gpui::test]
 9380async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9381    init_test(cx, |_| {});
 9382
 9383    let fs = FakeFs::new(cx.executor());
 9384    fs.insert_tree(
 9385        "/a",
 9386        json!({
 9387            "main.rs": "fn main() { let a = 5; }",
 9388            "other.rs": "// Test file",
 9389        }),
 9390    )
 9391    .await;
 9392
 9393    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9394
 9395    let server_restarts = Arc::new(AtomicUsize::new(0));
 9396    let closure_restarts = Arc::clone(&server_restarts);
 9397    let language_server_name = "test language server";
 9398    let language_name: Arc<str> = "Rust".into();
 9399
 9400    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9401    language_registry.add(Arc::new(Language::new(
 9402        LanguageConfig {
 9403            name: Arc::clone(&language_name),
 9404            matcher: LanguageMatcher {
 9405                path_suffixes: vec!["rs".to_string()],
 9406                ..Default::default()
 9407            },
 9408            ..Default::default()
 9409        },
 9410        Some(tree_sitter_rust::language()),
 9411    )));
 9412    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9413        "Rust",
 9414        FakeLspAdapter {
 9415            name: language_server_name,
 9416            initialization_options: Some(json!({
 9417                "testOptionValue": true
 9418            })),
 9419            initializer: Some(Box::new(move |fake_server| {
 9420                let task_restarts = Arc::clone(&closure_restarts);
 9421                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9422                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9423                    futures::future::ready(Ok(()))
 9424                });
 9425            })),
 9426            ..Default::default()
 9427        },
 9428    );
 9429
 9430    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9431    let _buffer = project
 9432        .update(cx, |project, cx| {
 9433            project.open_local_buffer("/a/main.rs", cx)
 9434        })
 9435        .await
 9436        .unwrap();
 9437    let _fake_server = fake_servers.next().await.unwrap();
 9438    update_test_language_settings(cx, |language_settings| {
 9439        language_settings.languages.insert(
 9440            Arc::clone(&language_name),
 9441            LanguageSettingsContent {
 9442                tab_size: NonZeroU32::new(8),
 9443                ..Default::default()
 9444            },
 9445        );
 9446    });
 9447    cx.executor().run_until_parked();
 9448    assert_eq!(
 9449        server_restarts.load(atomic::Ordering::Acquire),
 9450        0,
 9451        "Should not restart LSP server on an unrelated change"
 9452    );
 9453
 9454    update_test_project_settings(cx, |project_settings| {
 9455        project_settings.lsp.insert(
 9456            "Some other server name".into(),
 9457            LspSettings {
 9458                binary: None,
 9459                settings: None,
 9460                initialization_options: Some(json!({
 9461                    "some other init value": false
 9462                })),
 9463            },
 9464        );
 9465    });
 9466    cx.executor().run_until_parked();
 9467    assert_eq!(
 9468        server_restarts.load(atomic::Ordering::Acquire),
 9469        0,
 9470        "Should not restart LSP server on an unrelated LSP settings change"
 9471    );
 9472
 9473    update_test_project_settings(cx, |project_settings| {
 9474        project_settings.lsp.insert(
 9475            language_server_name.into(),
 9476            LspSettings {
 9477                binary: None,
 9478                settings: None,
 9479                initialization_options: Some(json!({
 9480                    "anotherInitValue": false
 9481                })),
 9482            },
 9483        );
 9484    });
 9485    cx.executor().run_until_parked();
 9486    assert_eq!(
 9487        server_restarts.load(atomic::Ordering::Acquire),
 9488        1,
 9489        "Should restart LSP server on a related LSP settings change"
 9490    );
 9491
 9492    update_test_project_settings(cx, |project_settings| {
 9493        project_settings.lsp.insert(
 9494            language_server_name.into(),
 9495            LspSettings {
 9496                binary: None,
 9497                settings: None,
 9498                initialization_options: Some(json!({
 9499                    "anotherInitValue": false
 9500                })),
 9501            },
 9502        );
 9503    });
 9504    cx.executor().run_until_parked();
 9505    assert_eq!(
 9506        server_restarts.load(atomic::Ordering::Acquire),
 9507        1,
 9508        "Should not restart LSP server on a related LSP settings change that is the same"
 9509    );
 9510
 9511    update_test_project_settings(cx, |project_settings| {
 9512        project_settings.lsp.insert(
 9513            language_server_name.into(),
 9514            LspSettings {
 9515                binary: None,
 9516                settings: None,
 9517                initialization_options: None,
 9518            },
 9519        );
 9520    });
 9521    cx.executor().run_until_parked();
 9522    assert_eq!(
 9523        server_restarts.load(atomic::Ordering::Acquire),
 9524        2,
 9525        "Should restart LSP server on another related LSP settings change"
 9526    );
 9527}
 9528
 9529#[gpui::test]
 9530async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9531    init_test(cx, |_| {});
 9532
 9533    let mut cx = EditorLspTestContext::new_rust(
 9534        lsp::ServerCapabilities {
 9535            completion_provider: Some(lsp::CompletionOptions {
 9536                trigger_characters: Some(vec![".".to_string()]),
 9537                resolve_provider: Some(true),
 9538                ..Default::default()
 9539            }),
 9540            ..Default::default()
 9541        },
 9542        cx,
 9543    )
 9544    .await;
 9545
 9546    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9547    cx.simulate_keystroke(".");
 9548    let completion_item = lsp::CompletionItem {
 9549        label: "some".into(),
 9550        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9551        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9552        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9553            kind: lsp::MarkupKind::Markdown,
 9554            value: "```rust\nSome(2)\n```".to_string(),
 9555        })),
 9556        deprecated: Some(false),
 9557        sort_text: Some("fffffff2".to_string()),
 9558        filter_text: Some("some".to_string()),
 9559        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9560        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9561            range: lsp::Range {
 9562                start: lsp::Position {
 9563                    line: 0,
 9564                    character: 22,
 9565                },
 9566                end: lsp::Position {
 9567                    line: 0,
 9568                    character: 22,
 9569                },
 9570            },
 9571            new_text: "Some(2)".to_string(),
 9572        })),
 9573        additional_text_edits: Some(vec![lsp::TextEdit {
 9574            range: lsp::Range {
 9575                start: lsp::Position {
 9576                    line: 0,
 9577                    character: 20,
 9578                },
 9579                end: lsp::Position {
 9580                    line: 0,
 9581                    character: 22,
 9582                },
 9583            },
 9584            new_text: "".to_string(),
 9585        }]),
 9586        ..Default::default()
 9587    };
 9588
 9589    let closure_completion_item = completion_item.clone();
 9590    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9591        let task_completion_item = closure_completion_item.clone();
 9592        async move {
 9593            Ok(Some(lsp::CompletionResponse::Array(vec![
 9594                task_completion_item,
 9595            ])))
 9596        }
 9597    });
 9598
 9599    request.next().await;
 9600
 9601    cx.condition(|editor, _| editor.context_menu_visible())
 9602        .await;
 9603    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9604        editor
 9605            .confirm_completion(&ConfirmCompletion::default(), cx)
 9606            .unwrap()
 9607    });
 9608    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9609
 9610    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9611        let task_completion_item = completion_item.clone();
 9612        async move { Ok(task_completion_item) }
 9613    })
 9614    .next()
 9615    .await
 9616    .unwrap();
 9617    apply_additional_edits.await.unwrap();
 9618    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9619}
 9620
 9621#[gpui::test]
 9622async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9623    init_test(cx, |_| {});
 9624
 9625    let mut cx = EditorLspTestContext::new(
 9626        Language::new(
 9627            LanguageConfig {
 9628                matcher: LanguageMatcher {
 9629                    path_suffixes: vec!["jsx".into()],
 9630                    ..Default::default()
 9631                },
 9632                overrides: [(
 9633                    "element".into(),
 9634                    LanguageConfigOverride {
 9635                        word_characters: Override::Set(['-'].into_iter().collect()),
 9636                        ..Default::default()
 9637                    },
 9638                )]
 9639                .into_iter()
 9640                .collect(),
 9641                ..Default::default()
 9642            },
 9643            Some(tree_sitter_typescript::language_tsx()),
 9644        )
 9645        .with_override_query("(jsx_self_closing_element) @element")
 9646        .unwrap(),
 9647        lsp::ServerCapabilities {
 9648            completion_provider: Some(lsp::CompletionOptions {
 9649                trigger_characters: Some(vec![":".to_string()]),
 9650                ..Default::default()
 9651            }),
 9652            ..Default::default()
 9653        },
 9654        cx,
 9655    )
 9656    .await;
 9657
 9658    cx.lsp
 9659        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9660            Ok(Some(lsp::CompletionResponse::Array(vec![
 9661                lsp::CompletionItem {
 9662                    label: "bg-blue".into(),
 9663                    ..Default::default()
 9664                },
 9665                lsp::CompletionItem {
 9666                    label: "bg-red".into(),
 9667                    ..Default::default()
 9668                },
 9669                lsp::CompletionItem {
 9670                    label: "bg-yellow".into(),
 9671                    ..Default::default()
 9672                },
 9673            ])))
 9674        });
 9675
 9676    cx.set_state(r#"<p class="bgˇ" />"#);
 9677
 9678    // Trigger completion when typing a dash, because the dash is an extra
 9679    // word character in the 'element' scope, which contains the cursor.
 9680    cx.simulate_keystroke("-");
 9681    cx.executor().run_until_parked();
 9682    cx.update_editor(|editor, _| {
 9683        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9684            assert_eq!(
 9685                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9686                &["bg-red", "bg-blue", "bg-yellow"]
 9687            );
 9688        } else {
 9689            panic!("expected completion menu to be open");
 9690        }
 9691    });
 9692
 9693    cx.simulate_keystroke("l");
 9694    cx.executor().run_until_parked();
 9695    cx.update_editor(|editor, _| {
 9696        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9697            assert_eq!(
 9698                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9699                &["bg-blue", "bg-yellow"]
 9700            );
 9701        } else {
 9702            panic!("expected completion menu to be open");
 9703        }
 9704    });
 9705
 9706    // When filtering completions, consider the character after the '-' to
 9707    // be the start of a subword.
 9708    cx.set_state(r#"<p class="yelˇ" />"#);
 9709    cx.simulate_keystroke("l");
 9710    cx.executor().run_until_parked();
 9711    cx.update_editor(|editor, _| {
 9712        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9713            assert_eq!(
 9714                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9715                &["bg-yellow"]
 9716            );
 9717        } else {
 9718            panic!("expected completion menu to be open");
 9719        }
 9720    });
 9721}
 9722
 9723#[gpui::test]
 9724async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9725    init_test(cx, |settings| {
 9726        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 9727    });
 9728
 9729    let fs = FakeFs::new(cx.executor());
 9730    fs.insert_file("/file.ts", Default::default()).await;
 9731
 9732    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9733    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9734
 9735    language_registry.add(Arc::new(Language::new(
 9736        LanguageConfig {
 9737            name: "TypeScript".into(),
 9738            matcher: LanguageMatcher {
 9739                path_suffixes: vec!["ts".to_string()],
 9740                ..Default::default()
 9741            },
 9742            ..Default::default()
 9743        },
 9744        Some(tree_sitter_rust::language()),
 9745    )));
 9746    update_test_language_settings(cx, |settings| {
 9747        settings.defaults.prettier = Some(PrettierSettings {
 9748            allowed: true,
 9749            ..PrettierSettings::default()
 9750        });
 9751    });
 9752
 9753    let test_plugin = "test_plugin";
 9754    let _ = language_registry.register_fake_lsp_adapter(
 9755        "TypeScript",
 9756        FakeLspAdapter {
 9757            prettier_plugins: vec![test_plugin],
 9758            ..Default::default()
 9759        },
 9760    );
 9761
 9762    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9763    let buffer = project
 9764        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9765        .await
 9766        .unwrap();
 9767
 9768    let buffer_text = "one\ntwo\nthree\n";
 9769    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9770    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9771    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9772
 9773    editor
 9774        .update(cx, |editor, cx| {
 9775            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9776        })
 9777        .unwrap()
 9778        .await;
 9779    assert_eq!(
 9780        editor.update(cx, |editor, cx| editor.text(cx)),
 9781        buffer_text.to_string() + prettier_format_suffix,
 9782        "Test prettier formatting was not applied to the original buffer text",
 9783    );
 9784
 9785    update_test_language_settings(cx, |settings| {
 9786        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 9787    });
 9788    let format = editor.update(cx, |editor, cx| {
 9789        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9790    });
 9791    format.await.unwrap();
 9792    assert_eq!(
 9793        editor.update(cx, |editor, cx| editor.text(cx)),
 9794        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9795        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9796    );
 9797}
 9798
 9799#[gpui::test]
 9800async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9801    init_test(cx, |_| {});
 9802    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9803    let base_text = indoc! {r#"struct Row;
 9804struct Row1;
 9805struct Row2;
 9806
 9807struct Row4;
 9808struct Row5;
 9809struct Row6;
 9810
 9811struct Row8;
 9812struct Row9;
 9813struct Row10;"#};
 9814
 9815    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9816    assert_hunk_revert(
 9817        indoc! {r#"struct Row;
 9818                   struct Row1;
 9819                   struct Row1.1;
 9820                   struct Row1.2;
 9821                   struct Row2;ˇ
 9822
 9823                   struct Row4;
 9824                   struct Row5;
 9825                   struct Row6;
 9826
 9827                   struct Row8;
 9828                   ˇstruct Row9;
 9829                   struct Row9.1;
 9830                   struct Row9.2;
 9831                   struct Row9.3;
 9832                   struct Row10;"#},
 9833        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9834        indoc! {r#"struct Row;
 9835                   struct Row1;
 9836                   struct Row1.1;
 9837                   struct Row1.2;
 9838                   struct Row2;ˇ
 9839
 9840                   struct Row4;
 9841                   struct Row5;
 9842                   struct Row6;
 9843
 9844                   struct Row8;
 9845                   ˇstruct Row9;
 9846                   struct Row9.1;
 9847                   struct Row9.2;
 9848                   struct Row9.3;
 9849                   struct Row10;"#},
 9850        base_text,
 9851        &mut cx,
 9852    );
 9853    // Same for selections
 9854    assert_hunk_revert(
 9855        indoc! {r#"struct Row;
 9856                   struct Row1;
 9857                   struct Row2;
 9858                   struct Row2.1;
 9859                   struct Row2.2;
 9860                   «ˇ
 9861                   struct Row4;
 9862                   struct» Row5;
 9863                   «struct Row6;
 9864                   ˇ»
 9865                   struct Row9.1;
 9866                   struct Row9.2;
 9867                   struct Row9.3;
 9868                   struct Row8;
 9869                   struct Row9;
 9870                   struct Row10;"#},
 9871        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9872        indoc! {r#"struct Row;
 9873                   struct Row1;
 9874                   struct Row2;
 9875                   struct Row2.1;
 9876                   struct Row2.2;
 9877                   «ˇ
 9878                   struct Row4;
 9879                   struct» Row5;
 9880                   «struct Row6;
 9881                   ˇ»
 9882                   struct Row9.1;
 9883                   struct Row9.2;
 9884                   struct Row9.3;
 9885                   struct Row8;
 9886                   struct Row9;
 9887                   struct Row10;"#},
 9888        base_text,
 9889        &mut cx,
 9890    );
 9891
 9892    // When carets and selections intersect the addition hunks, those are reverted.
 9893    // Adjacent carets got merged.
 9894    assert_hunk_revert(
 9895        indoc! {r#"struct Row;
 9896                   ˇ// something on the top
 9897                   struct Row1;
 9898                   struct Row2;
 9899                   struct Roˇw3.1;
 9900                   struct Row2.2;
 9901                   struct Row2.3;ˇ
 9902
 9903                   struct Row4;
 9904                   struct ˇRow5.1;
 9905                   struct Row5.2;
 9906                   struct «Rowˇ»5.3;
 9907                   struct Row5;
 9908                   struct Row6;
 9909                   ˇ
 9910                   struct Row9.1;
 9911                   struct «Rowˇ»9.2;
 9912                   struct «ˇRow»9.3;
 9913                   struct Row8;
 9914                   struct Row9;
 9915                   «ˇ// something on bottom»
 9916                   struct Row10;"#},
 9917        vec![
 9918            DiffHunkStatus::Added,
 9919            DiffHunkStatus::Added,
 9920            DiffHunkStatus::Added,
 9921            DiffHunkStatus::Added,
 9922            DiffHunkStatus::Added,
 9923        ],
 9924        indoc! {r#"struct Row;
 9925                   ˇstruct Row1;
 9926                   struct Row2;
 9927                   ˇ
 9928                   struct Row4;
 9929                   ˇstruct Row5;
 9930                   struct Row6;
 9931                   ˇ
 9932                   ˇstruct Row8;
 9933                   struct Row9;
 9934                   ˇstruct Row10;"#},
 9935        base_text,
 9936        &mut cx,
 9937    );
 9938}
 9939
 9940#[gpui::test]
 9941async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9942    init_test(cx, |_| {});
 9943    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9944    let base_text = indoc! {r#"struct Row;
 9945struct Row1;
 9946struct Row2;
 9947
 9948struct Row4;
 9949struct Row5;
 9950struct Row6;
 9951
 9952struct Row8;
 9953struct Row9;
 9954struct Row10;"#};
 9955
 9956    // Modification hunks behave the same as the addition ones.
 9957    assert_hunk_revert(
 9958        indoc! {r#"struct Row;
 9959                   struct Row1;
 9960                   struct Row33;
 9961                   ˇ
 9962                   struct Row4;
 9963                   struct Row5;
 9964                   struct Row6;
 9965                   ˇ
 9966                   struct Row99;
 9967                   struct Row9;
 9968                   struct Row10;"#},
 9969        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9970        indoc! {r#"struct Row;
 9971                   struct Row1;
 9972                   struct Row33;
 9973                   ˇ
 9974                   struct Row4;
 9975                   struct Row5;
 9976                   struct Row6;
 9977                   ˇ
 9978                   struct Row99;
 9979                   struct Row9;
 9980                   struct Row10;"#},
 9981        base_text,
 9982        &mut cx,
 9983    );
 9984    assert_hunk_revert(
 9985        indoc! {r#"struct Row;
 9986                   struct Row1;
 9987                   struct Row33;
 9988                   «ˇ
 9989                   struct Row4;
 9990                   struct» Row5;
 9991                   «struct Row6;
 9992                   ˇ»
 9993                   struct Row99;
 9994                   struct Row9;
 9995                   struct Row10;"#},
 9996        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9997        indoc! {r#"struct Row;
 9998                   struct Row1;
 9999                   struct Row33;
10000                   «ˇ
10001                   struct Row4;
10002                   struct» Row5;
10003                   «struct Row6;
10004                   ˇ»
10005                   struct Row99;
10006                   struct Row9;
10007                   struct Row10;"#},
10008        base_text,
10009        &mut cx,
10010    );
10011
10012    assert_hunk_revert(
10013        indoc! {r#"ˇstruct Row1.1;
10014                   struct Row1;
10015                   «ˇstr»uct Row22;
10016
10017                   struct ˇRow44;
10018                   struct Row5;
10019                   struct «Rˇ»ow66;ˇ
10020
10021                   «struˇ»ct Row88;
10022                   struct Row9;
10023                   struct Row1011;ˇ"#},
10024        vec![
10025            DiffHunkStatus::Modified,
10026            DiffHunkStatus::Modified,
10027            DiffHunkStatus::Modified,
10028            DiffHunkStatus::Modified,
10029            DiffHunkStatus::Modified,
10030            DiffHunkStatus::Modified,
10031        ],
10032        indoc! {r#"struct Row;
10033                   ˇstruct Row1;
10034                   struct Row2;
10035                   ˇ
10036                   struct Row4;
10037                   ˇstruct Row5;
10038                   struct Row6;
10039                   ˇ
10040                   struct Row8;
10041                   ˇstruct Row9;
10042                   struct Row10;ˇ"#},
10043        base_text,
10044        &mut cx,
10045    );
10046}
10047
10048#[gpui::test]
10049async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10050    init_test(cx, |_| {});
10051    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10052    let base_text = indoc! {r#"struct Row;
10053struct Row1;
10054struct Row2;
10055
10056struct Row4;
10057struct Row5;
10058struct Row6;
10059
10060struct Row8;
10061struct Row9;
10062struct Row10;"#};
10063
10064    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
10065    assert_hunk_revert(
10066        indoc! {r#"struct Row;
10067                   struct Row2;
10068
10069                   ˇstruct Row4;
10070                   struct Row5;
10071                   struct Row6;
10072                   ˇ
10073                   struct Row8;
10074                   struct Row10;"#},
10075        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10076        indoc! {r#"struct Row;
10077                   struct Row2;
10078
10079                   ˇstruct Row4;
10080                   struct Row5;
10081                   struct Row6;
10082                   ˇ
10083                   struct Row8;
10084                   struct Row10;"#},
10085        base_text,
10086        &mut cx,
10087    );
10088    assert_hunk_revert(
10089        indoc! {r#"struct Row;
10090                   struct Row2;
10091
10092                   «ˇstruct Row4;
10093                   struct» Row5;
10094                   «struct Row6;
10095                   ˇ»
10096                   struct Row8;
10097                   struct Row10;"#},
10098        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10099        indoc! {r#"struct Row;
10100                   struct Row2;
10101
10102                   «ˇstruct Row4;
10103                   struct» Row5;
10104                   «struct Row6;
10105                   ˇ»
10106                   struct Row8;
10107                   struct Row10;"#},
10108        base_text,
10109        &mut cx,
10110    );
10111
10112    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10113    assert_hunk_revert(
10114        indoc! {r#"struct Row;
10115                   ˇstruct Row2;
10116
10117                   struct Row4;
10118                   struct Row5;
10119                   struct Row6;
10120
10121                   struct Row8;ˇ
10122                   struct Row10;"#},
10123        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10124        indoc! {r#"struct Row;
10125                   struct Row1;
10126                   ˇstruct Row2;
10127
10128                   struct Row4;
10129                   struct Row5;
10130                   struct Row6;
10131
10132                   struct Row8;ˇ
10133                   struct Row9;
10134                   struct Row10;"#},
10135        base_text,
10136        &mut cx,
10137    );
10138    assert_hunk_revert(
10139        indoc! {r#"struct Row;
10140                   struct Row2«ˇ;
10141                   struct Row4;
10142                   struct» Row5;
10143                   «struct Row6;
10144
10145                   struct Row8;ˇ»
10146                   struct Row10;"#},
10147        vec![
10148            DiffHunkStatus::Removed,
10149            DiffHunkStatus::Removed,
10150            DiffHunkStatus::Removed,
10151        ],
10152        indoc! {r#"struct Row;
10153                   struct Row1;
10154                   struct Row2«ˇ;
10155
10156                   struct Row4;
10157                   struct» Row5;
10158                   «struct Row6;
10159
10160                   struct Row8;ˇ»
10161                   struct Row9;
10162                   struct Row10;"#},
10163        base_text,
10164        &mut cx,
10165    );
10166}
10167
10168#[gpui::test]
10169async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10170    init_test(cx, |_| {});
10171
10172    let cols = 4;
10173    let rows = 10;
10174    let sample_text_1 = sample_text(rows, cols, 'a');
10175    assert_eq!(
10176        sample_text_1,
10177        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10178    );
10179    let sample_text_2 = sample_text(rows, cols, 'l');
10180    assert_eq!(
10181        sample_text_2,
10182        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10183    );
10184    let sample_text_3 = sample_text(rows, cols, 'v');
10185    assert_eq!(
10186        sample_text_3,
10187        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10188    );
10189
10190    fn diff_every_buffer_row(
10191        buffer: &Model<Buffer>,
10192        sample_text: String,
10193        cols: usize,
10194        cx: &mut gpui::TestAppContext,
10195    ) {
10196        // revert first character in each row, creating one large diff hunk per buffer
10197        let is_first_char = |offset: usize| offset % cols == 0;
10198        buffer.update(cx, |buffer, cx| {
10199            buffer.set_text(
10200                sample_text
10201                    .chars()
10202                    .enumerate()
10203                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10204                    .collect::<String>(),
10205                cx,
10206            );
10207            buffer.set_diff_base(Some(sample_text), cx);
10208        });
10209        cx.executor().run_until_parked();
10210    }
10211
10212    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10213    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10214
10215    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10216    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10217
10218    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10219    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10220
10221    let multibuffer = cx.new_model(|cx| {
10222        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10223        multibuffer.push_excerpts(
10224            buffer_1.clone(),
10225            [
10226                ExcerptRange {
10227                    context: Point::new(0, 0)..Point::new(3, 0),
10228                    primary: None,
10229                },
10230                ExcerptRange {
10231                    context: Point::new(5, 0)..Point::new(7, 0),
10232                    primary: None,
10233                },
10234                ExcerptRange {
10235                    context: Point::new(9, 0)..Point::new(10, 4),
10236                    primary: None,
10237                },
10238            ],
10239            cx,
10240        );
10241        multibuffer.push_excerpts(
10242            buffer_2.clone(),
10243            [
10244                ExcerptRange {
10245                    context: Point::new(0, 0)..Point::new(3, 0),
10246                    primary: None,
10247                },
10248                ExcerptRange {
10249                    context: Point::new(5, 0)..Point::new(7, 0),
10250                    primary: None,
10251                },
10252                ExcerptRange {
10253                    context: Point::new(9, 0)..Point::new(10, 4),
10254                    primary: None,
10255                },
10256            ],
10257            cx,
10258        );
10259        multibuffer.push_excerpts(
10260            buffer_3.clone(),
10261            [
10262                ExcerptRange {
10263                    context: Point::new(0, 0)..Point::new(3, 0),
10264                    primary: None,
10265                },
10266                ExcerptRange {
10267                    context: Point::new(5, 0)..Point::new(7, 0),
10268                    primary: None,
10269                },
10270                ExcerptRange {
10271                    context: Point::new(9, 0)..Point::new(10, 4),
10272                    primary: None,
10273                },
10274            ],
10275            cx,
10276        );
10277        multibuffer
10278    });
10279
10280    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10281    editor.update(cx, |editor, cx| {
10282        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
10283        editor.select_all(&SelectAll, cx);
10284        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10285    });
10286    cx.executor().run_until_parked();
10287    // When all ranges are selected, all buffer hunks are reverted.
10288    editor.update(cx, |editor, cx| {
10289        assert_eq!(editor.text(cx), "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n");
10290    });
10291    buffer_1.update(cx, |buffer, _| {
10292        assert_eq!(buffer.text(), sample_text_1);
10293    });
10294    buffer_2.update(cx, |buffer, _| {
10295        assert_eq!(buffer.text(), sample_text_2);
10296    });
10297    buffer_3.update(cx, |buffer, _| {
10298        assert_eq!(buffer.text(), sample_text_3);
10299    });
10300
10301    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10302    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10303    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10304    editor.update(cx, |editor, cx| {
10305        editor.change_selections(None, cx, |s| {
10306            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10307        });
10308        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10309    });
10310    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10311    // but not affect buffer_2 and its related excerpts.
10312    editor.update(cx, |editor, cx| {
10313        assert_eq!(
10314            editor.text(cx),
10315            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
10316        );
10317    });
10318    buffer_1.update(cx, |buffer, _| {
10319        assert_eq!(buffer.text(), sample_text_1);
10320    });
10321    buffer_2.update(cx, |buffer, _| {
10322        assert_eq!(
10323            buffer.text(),
10324            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10325        );
10326    });
10327    buffer_3.update(cx, |buffer, _| {
10328        assert_eq!(
10329            buffer.text(),
10330            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10331        );
10332    });
10333}
10334
10335#[gpui::test]
10336async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10337    init_test(cx, |_| {});
10338
10339    let cols = 4;
10340    let rows = 10;
10341    let sample_text_1 = sample_text(rows, cols, 'a');
10342    assert_eq!(
10343        sample_text_1,
10344        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10345    );
10346    let sample_text_2 = sample_text(rows, cols, 'l');
10347    assert_eq!(
10348        sample_text_2,
10349        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10350    );
10351    let sample_text_3 = sample_text(rows, cols, 'v');
10352    assert_eq!(
10353        sample_text_3,
10354        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10355    );
10356
10357    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10358    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10359    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10360
10361    let multi_buffer = cx.new_model(|cx| {
10362        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10363        multibuffer.push_excerpts(
10364            buffer_1.clone(),
10365            [
10366                ExcerptRange {
10367                    context: Point::new(0, 0)..Point::new(3, 0),
10368                    primary: None,
10369                },
10370                ExcerptRange {
10371                    context: Point::new(5, 0)..Point::new(7, 0),
10372                    primary: None,
10373                },
10374                ExcerptRange {
10375                    context: Point::new(9, 0)..Point::new(10, 4),
10376                    primary: None,
10377                },
10378            ],
10379            cx,
10380        );
10381        multibuffer.push_excerpts(
10382            buffer_2.clone(),
10383            [
10384                ExcerptRange {
10385                    context: Point::new(0, 0)..Point::new(3, 0),
10386                    primary: None,
10387                },
10388                ExcerptRange {
10389                    context: Point::new(5, 0)..Point::new(7, 0),
10390                    primary: None,
10391                },
10392                ExcerptRange {
10393                    context: Point::new(9, 0)..Point::new(10, 4),
10394                    primary: None,
10395                },
10396            ],
10397            cx,
10398        );
10399        multibuffer.push_excerpts(
10400            buffer_3.clone(),
10401            [
10402                ExcerptRange {
10403                    context: Point::new(0, 0)..Point::new(3, 0),
10404                    primary: None,
10405                },
10406                ExcerptRange {
10407                    context: Point::new(5, 0)..Point::new(7, 0),
10408                    primary: None,
10409                },
10410                ExcerptRange {
10411                    context: Point::new(9, 0)..Point::new(10, 4),
10412                    primary: None,
10413                },
10414            ],
10415            cx,
10416        );
10417        multibuffer
10418    });
10419
10420    let fs = FakeFs::new(cx.executor());
10421    fs.insert_tree(
10422        "/a",
10423        json!({
10424            "main.rs": sample_text_1,
10425            "other.rs": sample_text_2,
10426            "lib.rs": sample_text_3,
10427        }),
10428    )
10429    .await;
10430    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10431    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10432    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10433    let multi_buffer_editor = cx.new_view(|cx| {
10434        Editor::new(
10435            EditorMode::Full,
10436            multi_buffer,
10437            Some(project.clone()),
10438            true,
10439            cx,
10440        )
10441    });
10442    let multibuffer_item_id = workspace
10443        .update(cx, |workspace, cx| {
10444            assert!(
10445                workspace.active_item(cx).is_none(),
10446                "active item should be None before the first item is added"
10447            );
10448            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
10449            let active_item = workspace
10450                .active_item(cx)
10451                .expect("should have an active item after adding the multi buffer");
10452            assert!(
10453                !active_item.is_singleton(cx),
10454                "A multi buffer was expected to active after adding"
10455            );
10456            active_item.item_id()
10457        })
10458        .unwrap();
10459    cx.executor().run_until_parked();
10460
10461    multi_buffer_editor.update(cx, |editor, cx| {
10462        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10463        editor.open_excerpts(&OpenExcerpts, cx);
10464    });
10465    cx.executor().run_until_parked();
10466    let first_item_id = workspace
10467        .update(cx, |workspace, cx| {
10468            let active_item = workspace
10469                .active_item(cx)
10470                .expect("should have an active item after navigating into the 1st buffer");
10471            let first_item_id = active_item.item_id();
10472            assert_ne!(
10473                first_item_id, multibuffer_item_id,
10474                "Should navigate into the 1st buffer and activate it"
10475            );
10476            assert!(
10477                active_item.is_singleton(cx),
10478                "New active item should be a singleton buffer"
10479            );
10480            assert_eq!(
10481                active_item
10482                    .act_as::<Editor>(cx)
10483                    .expect("should have navigated into an editor for the 1st buffer")
10484                    .read(cx)
10485                    .text(cx),
10486                sample_text_1
10487            );
10488
10489            workspace
10490                .go_back(workspace.active_pane().downgrade(), cx)
10491                .detach_and_log_err(cx);
10492
10493            first_item_id
10494        })
10495        .unwrap();
10496    cx.executor().run_until_parked();
10497    workspace
10498        .update(cx, |workspace, cx| {
10499            let active_item = workspace
10500                .active_item(cx)
10501                .expect("should have an active item after navigating back");
10502            assert_eq!(
10503                active_item.item_id(),
10504                multibuffer_item_id,
10505                "Should navigate back to the multi buffer"
10506            );
10507            assert!(!active_item.is_singleton(cx));
10508        })
10509        .unwrap();
10510
10511    multi_buffer_editor.update(cx, |editor, cx| {
10512        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10513            s.select_ranges(Some(39..40))
10514        });
10515        editor.open_excerpts(&OpenExcerpts, cx);
10516    });
10517    cx.executor().run_until_parked();
10518    let second_item_id = workspace
10519        .update(cx, |workspace, cx| {
10520            let active_item = workspace
10521                .active_item(cx)
10522                .expect("should have an active item after navigating into the 2nd buffer");
10523            let second_item_id = active_item.item_id();
10524            assert_ne!(
10525                second_item_id, multibuffer_item_id,
10526                "Should navigate away from the multibuffer"
10527            );
10528            assert_ne!(
10529                second_item_id, first_item_id,
10530                "Should navigate into the 2nd buffer and activate it"
10531            );
10532            assert!(
10533                active_item.is_singleton(cx),
10534                "New active item should be a singleton buffer"
10535            );
10536            assert_eq!(
10537                active_item
10538                    .act_as::<Editor>(cx)
10539                    .expect("should have navigated into an editor")
10540                    .read(cx)
10541                    .text(cx),
10542                sample_text_2
10543            );
10544
10545            workspace
10546                .go_back(workspace.active_pane().downgrade(), cx)
10547                .detach_and_log_err(cx);
10548
10549            second_item_id
10550        })
10551        .unwrap();
10552    cx.executor().run_until_parked();
10553    workspace
10554        .update(cx, |workspace, cx| {
10555            let active_item = workspace
10556                .active_item(cx)
10557                .expect("should have an active item after navigating back from the 2nd buffer");
10558            assert_eq!(
10559                active_item.item_id(),
10560                multibuffer_item_id,
10561                "Should navigate back from the 2nd buffer to the multi buffer"
10562            );
10563            assert!(!active_item.is_singleton(cx));
10564        })
10565        .unwrap();
10566
10567    multi_buffer_editor.update(cx, |editor, cx| {
10568        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10569            s.select_ranges(Some(60..70))
10570        });
10571        editor.open_excerpts(&OpenExcerpts, cx);
10572    });
10573    cx.executor().run_until_parked();
10574    workspace
10575        .update(cx, |workspace, cx| {
10576            let active_item = workspace
10577                .active_item(cx)
10578                .expect("should have an active item after navigating into the 3rd buffer");
10579            let third_item_id = active_item.item_id();
10580            assert_ne!(
10581                third_item_id, multibuffer_item_id,
10582                "Should navigate into the 3rd buffer and activate it"
10583            );
10584            assert_ne!(third_item_id, first_item_id);
10585            assert_ne!(third_item_id, second_item_id);
10586            assert!(
10587                active_item.is_singleton(cx),
10588                "New active item should be a singleton buffer"
10589            );
10590            assert_eq!(
10591                active_item
10592                    .act_as::<Editor>(cx)
10593                    .expect("should have navigated into an editor")
10594                    .read(cx)
10595                    .text(cx),
10596                sample_text_3
10597            );
10598
10599            workspace
10600                .go_back(workspace.active_pane().downgrade(), cx)
10601                .detach_and_log_err(cx);
10602        })
10603        .unwrap();
10604    cx.executor().run_until_parked();
10605    workspace
10606        .update(cx, |workspace, cx| {
10607            let active_item = workspace
10608                .active_item(cx)
10609                .expect("should have an active item after navigating back from the 3rd buffer");
10610            assert_eq!(
10611                active_item.item_id(),
10612                multibuffer_item_id,
10613                "Should navigate back from the 3rd buffer to the multi buffer"
10614            );
10615            assert!(!active_item.is_singleton(cx));
10616        })
10617        .unwrap();
10618}
10619
10620#[gpui::test]
10621async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10622    init_test(cx, |_| {});
10623
10624    let mut cx = EditorTestContext::new(cx).await;
10625
10626    let diff_base = r#"
10627        use some::mod;
10628
10629        const A: u32 = 42;
10630
10631        fn main() {
10632            println!("hello");
10633
10634            println!("world");
10635        }
10636        "#
10637    .unindent();
10638
10639    cx.set_state(
10640        &r#"
10641        use some::modified;
10642
10643        ˇ
10644        fn main() {
10645            println!("hello there");
10646
10647            println!("around the");
10648            println!("world");
10649        }
10650        "#
10651        .unindent(),
10652    );
10653
10654    cx.set_diff_base(Some(&diff_base));
10655    executor.run_until_parked();
10656    let unexpanded_hunks = vec![
10657        (
10658            "use some::mod;\n".to_string(),
10659            DiffHunkStatus::Modified,
10660            DisplayRow(0)..DisplayRow(1),
10661        ),
10662        (
10663            "const A: u32 = 42;\n".to_string(),
10664            DiffHunkStatus::Removed,
10665            DisplayRow(2)..DisplayRow(2),
10666        ),
10667        (
10668            "    println!(\"hello\");\n".to_string(),
10669            DiffHunkStatus::Modified,
10670            DisplayRow(4)..DisplayRow(5),
10671        ),
10672        (
10673            "".to_string(),
10674            DiffHunkStatus::Added,
10675            DisplayRow(6)..DisplayRow(7),
10676        ),
10677    ];
10678    cx.update_editor(|editor, cx| {
10679        let snapshot = editor.snapshot(cx);
10680        let all_hunks = editor_hunks(editor, &snapshot, cx);
10681        assert_eq!(all_hunks, unexpanded_hunks);
10682    });
10683
10684    cx.update_editor(|editor, cx| {
10685        for _ in 0..4 {
10686            editor.go_to_hunk(&GoToHunk, cx);
10687            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10688        }
10689    });
10690    executor.run_until_parked();
10691    cx.assert_editor_state(
10692        &r#"
10693        use some::modified;
10694
10695        ˇ
10696        fn main() {
10697            println!("hello there");
10698
10699            println!("around the");
10700            println!("world");
10701        }
10702        "#
10703        .unindent(),
10704    );
10705    cx.update_editor(|editor, cx| {
10706        let snapshot = editor.snapshot(cx);
10707        let all_hunks = editor_hunks(editor, &snapshot, cx);
10708        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10709        assert_eq!(
10710            expanded_hunks_background_highlights(editor, cx),
10711            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10712            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10713        );
10714        assert_eq!(
10715            all_hunks,
10716            vec![
10717                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10718                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10719                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10720                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10721            ],
10722            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10723            (from modified and removed hunks)"
10724        );
10725        assert_eq!(
10726            all_hunks, all_expanded_hunks,
10727            "Editor hunks should not change and all be expanded"
10728        );
10729    });
10730
10731    cx.update_editor(|editor, cx| {
10732        editor.cancel(&Cancel, cx);
10733
10734        let snapshot = editor.snapshot(cx);
10735        let all_hunks = editor_hunks(editor, &snapshot, cx);
10736        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10737        assert_eq!(
10738            expanded_hunks_background_highlights(editor, cx),
10739            Vec::new(),
10740            "After cancelling in editor, no git highlights should be left"
10741        );
10742        assert_eq!(
10743            all_expanded_hunks,
10744            Vec::new(),
10745            "After cancelling in editor, no hunks should be expanded"
10746        );
10747        assert_eq!(
10748            all_hunks, unexpanded_hunks,
10749            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10750        );
10751    });
10752}
10753
10754#[gpui::test]
10755async fn test_toggled_diff_base_change(
10756    executor: BackgroundExecutor,
10757    cx: &mut gpui::TestAppContext,
10758) {
10759    init_test(cx, |_| {});
10760
10761    let mut cx = EditorTestContext::new(cx).await;
10762
10763    let diff_base = r#"
10764        use some::mod1;
10765        use some::mod2;
10766
10767        const A: u32 = 42;
10768        const B: u32 = 42;
10769        const C: u32 = 42;
10770
10771        fn main(ˇ) {
10772            println!("hello");
10773
10774            println!("world");
10775        }
10776        "#
10777    .unindent();
10778
10779    cx.set_state(
10780        &r#"
10781        use some::mod2;
10782
10783        const A: u32 = 42;
10784        const C: u32 = 42;
10785
10786        fn main(ˇ) {
10787            //println!("hello");
10788
10789            println!("world");
10790            //
10791            //
10792        }
10793        "#
10794        .unindent(),
10795    );
10796
10797    cx.set_diff_base(Some(&diff_base));
10798    executor.run_until_parked();
10799    cx.update_editor(|editor, cx| {
10800        let snapshot = editor.snapshot(cx);
10801        let all_hunks = editor_hunks(editor, &snapshot, cx);
10802        assert_eq!(
10803            all_hunks,
10804            vec![
10805                (
10806                    "use some::mod1;\n".to_string(),
10807                    DiffHunkStatus::Removed,
10808                    DisplayRow(0)..DisplayRow(0)
10809                ),
10810                (
10811                    "const B: u32 = 42;\n".to_string(),
10812                    DiffHunkStatus::Removed,
10813                    DisplayRow(3)..DisplayRow(3)
10814                ),
10815                (
10816                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10817                    DiffHunkStatus::Modified,
10818                    DisplayRow(5)..DisplayRow(7)
10819                ),
10820                (
10821                    "".to_string(),
10822                    DiffHunkStatus::Added,
10823                    DisplayRow(9)..DisplayRow(11)
10824                ),
10825            ]
10826        );
10827    });
10828
10829    cx.update_editor(|editor, cx| {
10830        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10831    });
10832    executor.run_until_parked();
10833    cx.assert_editor_state(
10834        &r#"
10835        use some::mod2;
10836
10837        const A: u32 = 42;
10838        const C: u32 = 42;
10839
10840        fn main(ˇ) {
10841            //println!("hello");
10842
10843            println!("world");
10844            //
10845            //
10846        }
10847        "#
10848        .unindent(),
10849    );
10850    cx.update_editor(|editor, cx| {
10851        let snapshot = editor.snapshot(cx);
10852        let all_hunks = editor_hunks(editor, &snapshot, cx);
10853        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10854        assert_eq!(
10855            expanded_hunks_background_highlights(editor, cx),
10856            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10857            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10858        );
10859        assert_eq!(
10860            all_hunks,
10861            vec![
10862                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10863                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10864                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10865                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10866            ],
10867            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10868            (from modified and removed hunks)"
10869        );
10870        assert_eq!(
10871            all_hunks, all_expanded_hunks,
10872            "Editor hunks should not change and all be expanded"
10873        );
10874    });
10875
10876    cx.set_diff_base(Some("new diff base!"));
10877    executor.run_until_parked();
10878
10879    cx.update_editor(|editor, cx| {
10880        let snapshot = editor.snapshot(cx);
10881        let all_hunks = editor_hunks(editor, &snapshot, cx);
10882        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10883        assert_eq!(
10884            expanded_hunks_background_highlights(editor, cx),
10885            Vec::new(),
10886            "After diff base is changed, old git highlights should be removed"
10887        );
10888        assert_eq!(
10889            all_expanded_hunks,
10890            Vec::new(),
10891            "After diff base is changed, old git hunk expansions should be removed"
10892        );
10893        assert_eq!(
10894            all_hunks,
10895            vec![(
10896                "new diff base!".to_string(),
10897                DiffHunkStatus::Modified,
10898                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10899            )],
10900            "After diff base is changed, hunks should update"
10901        );
10902    });
10903}
10904
10905#[gpui::test]
10906async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10907    init_test(cx, |_| {});
10908
10909    let mut cx = EditorTestContext::new(cx).await;
10910
10911    let diff_base = r#"
10912        use some::mod1;
10913        use some::mod2;
10914
10915        const A: u32 = 42;
10916        const B: u32 = 42;
10917        const C: u32 = 42;
10918
10919        fn main(ˇ) {
10920            println!("hello");
10921
10922            println!("world");
10923        }
10924
10925        fn another() {
10926            println!("another");
10927        }
10928
10929        fn another2() {
10930            println!("another2");
10931        }
10932        "#
10933    .unindent();
10934
10935    cx.set_state(
10936        &r#"
10937        «use some::mod2;
10938
10939        const A: u32 = 42;
10940        const C: u32 = 42;
10941
10942        fn main() {
10943            //println!("hello");
10944
10945            println!("world");
10946            //
10947            //ˇ»
10948        }
10949
10950        fn another() {
10951            println!("another");
10952            println!("another");
10953        }
10954
10955            println!("another2");
10956        }
10957        "#
10958        .unindent(),
10959    );
10960
10961    cx.set_diff_base(Some(&diff_base));
10962    executor.run_until_parked();
10963    cx.update_editor(|editor, cx| {
10964        let snapshot = editor.snapshot(cx);
10965        let all_hunks = editor_hunks(editor, &snapshot, cx);
10966        assert_eq!(
10967            all_hunks,
10968            vec![
10969                (
10970                    "use some::mod1;\n".to_string(),
10971                    DiffHunkStatus::Removed,
10972                    DisplayRow(0)..DisplayRow(0)
10973                ),
10974                (
10975                    "const B: u32 = 42;\n".to_string(),
10976                    DiffHunkStatus::Removed,
10977                    DisplayRow(3)..DisplayRow(3)
10978                ),
10979                (
10980                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10981                    DiffHunkStatus::Modified,
10982                    DisplayRow(5)..DisplayRow(7)
10983                ),
10984                (
10985                    "".to_string(),
10986                    DiffHunkStatus::Added,
10987                    DisplayRow(9)..DisplayRow(11)
10988                ),
10989                (
10990                    "".to_string(),
10991                    DiffHunkStatus::Added,
10992                    DisplayRow(15)..DisplayRow(16)
10993                ),
10994                (
10995                    "fn another2() {\n".to_string(),
10996                    DiffHunkStatus::Removed,
10997                    DisplayRow(18)..DisplayRow(18)
10998                ),
10999            ]
11000        );
11001    });
11002
11003    cx.update_editor(|editor, cx| {
11004        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11005    });
11006    executor.run_until_parked();
11007    cx.assert_editor_state(
11008        &r#"
11009        «use some::mod2;
11010
11011        const A: u32 = 42;
11012        const C: u32 = 42;
11013
11014        fn main() {
11015            //println!("hello");
11016
11017            println!("world");
11018            //
11019            //ˇ»
11020        }
11021
11022        fn another() {
11023            println!("another");
11024            println!("another");
11025        }
11026
11027            println!("another2");
11028        }
11029        "#
11030        .unindent(),
11031    );
11032    cx.update_editor(|editor, cx| {
11033        let snapshot = editor.snapshot(cx);
11034        let all_hunks = editor_hunks(editor, &snapshot, cx);
11035        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11036        assert_eq!(
11037            expanded_hunks_background_highlights(editor, cx),
11038            vec![
11039                DisplayRow(9)..=DisplayRow(10),
11040                DisplayRow(13)..=DisplayRow(14),
11041                DisplayRow(19)..=DisplayRow(19)
11042            ]
11043        );
11044        assert_eq!(
11045            all_hunks,
11046            vec![
11047                (
11048                    "use some::mod1;\n".to_string(),
11049                    DiffHunkStatus::Removed,
11050                    DisplayRow(1)..DisplayRow(1)
11051                ),
11052                (
11053                    "const B: u32 = 42;\n".to_string(),
11054                    DiffHunkStatus::Removed,
11055                    DisplayRow(5)..DisplayRow(5)
11056                ),
11057                (
11058                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11059                    DiffHunkStatus::Modified,
11060                    DisplayRow(9)..DisplayRow(11)
11061                ),
11062                (
11063                    "".to_string(),
11064                    DiffHunkStatus::Added,
11065                    DisplayRow(13)..DisplayRow(15)
11066                ),
11067                (
11068                    "".to_string(),
11069                    DiffHunkStatus::Added,
11070                    DisplayRow(19)..DisplayRow(20)
11071                ),
11072                (
11073                    "fn another2() {\n".to_string(),
11074                    DiffHunkStatus::Removed,
11075                    DisplayRow(23)..DisplayRow(23)
11076                ),
11077            ],
11078        );
11079        assert_eq!(all_hunks, all_expanded_hunks);
11080    });
11081
11082    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11083    cx.executor().run_until_parked();
11084    cx.assert_editor_state(
11085        &r#"
11086        «use some::mod2;
11087
11088        const A: u32 = 42;
11089        const C: u32 = 42;
11090
11091        fn main() {
11092            //println!("hello");
11093
11094            println!("world");
11095            //
11096            //ˇ»
11097        }
11098
11099        fn another() {
11100            println!("another");
11101            println!("another");
11102        }
11103
11104            println!("another2");
11105        }
11106        "#
11107        .unindent(),
11108    );
11109    cx.update_editor(|editor, cx| {
11110        let snapshot = editor.snapshot(cx);
11111        let all_hunks = editor_hunks(editor, &snapshot, cx);
11112        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11113        assert_eq!(
11114            expanded_hunks_background_highlights(editor, cx),
11115            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11116            "Only one hunk is left not folded, its highlight should be visible"
11117        );
11118        assert_eq!(
11119            all_hunks,
11120            vec![
11121                (
11122                    "use some::mod1;\n".to_string(),
11123                    DiffHunkStatus::Removed,
11124                    DisplayRow(0)..DisplayRow(0)
11125                ),
11126                (
11127                    "const B: u32 = 42;\n".to_string(),
11128                    DiffHunkStatus::Removed,
11129                    DisplayRow(0)..DisplayRow(0)
11130                ),
11131                (
11132                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11133                    DiffHunkStatus::Modified,
11134                    DisplayRow(0)..DisplayRow(0)
11135                ),
11136                (
11137                    "".to_string(),
11138                    DiffHunkStatus::Added,
11139                    DisplayRow(0)..DisplayRow(1)
11140                ),
11141                (
11142                    "".to_string(),
11143                    DiffHunkStatus::Added,
11144                    DisplayRow(5)..DisplayRow(6)
11145                ),
11146                (
11147                    "fn another2() {\n".to_string(),
11148                    DiffHunkStatus::Removed,
11149                    DisplayRow(9)..DisplayRow(9)
11150                ),
11151            ],
11152            "Hunk list should still return shifted folded hunks"
11153        );
11154        assert_eq!(
11155            all_expanded_hunks,
11156            vec![
11157                (
11158                    "".to_string(),
11159                    DiffHunkStatus::Added,
11160                    DisplayRow(5)..DisplayRow(6)
11161                ),
11162                (
11163                    "fn another2() {\n".to_string(),
11164                    DiffHunkStatus::Removed,
11165                    DisplayRow(9)..DisplayRow(9)
11166                ),
11167            ],
11168            "Only non-folded hunks should be left expanded"
11169        );
11170    });
11171
11172    cx.update_editor(|editor, cx| {
11173        editor.select_all(&SelectAll, cx);
11174        editor.unfold_lines(&UnfoldLines, cx);
11175    });
11176    cx.executor().run_until_parked();
11177    cx.assert_editor_state(
11178        &r#"
11179        «use some::mod2;
11180
11181        const A: u32 = 42;
11182        const C: u32 = 42;
11183
11184        fn main() {
11185            //println!("hello");
11186
11187            println!("world");
11188            //
11189            //
11190        }
11191
11192        fn another() {
11193            println!("another");
11194            println!("another");
11195        }
11196
11197            println!("another2");
11198        }
11199        ˇ»"#
11200        .unindent(),
11201    );
11202    cx.update_editor(|editor, cx| {
11203        let snapshot = editor.snapshot(cx);
11204        let all_hunks = editor_hunks(editor, &snapshot, cx);
11205        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11206        assert_eq!(
11207            expanded_hunks_background_highlights(editor, cx),
11208            vec![
11209                DisplayRow(9)..=DisplayRow(10),
11210                DisplayRow(13)..=DisplayRow(14),
11211                DisplayRow(19)..=DisplayRow(19)
11212            ],
11213            "After unfolding, all hunk diffs should be visible again"
11214        );
11215        assert_eq!(
11216            all_hunks,
11217            vec![
11218                (
11219                    "use some::mod1;\n".to_string(),
11220                    DiffHunkStatus::Removed,
11221                    DisplayRow(1)..DisplayRow(1)
11222                ),
11223                (
11224                    "const B: u32 = 42;\n".to_string(),
11225                    DiffHunkStatus::Removed,
11226                    DisplayRow(5)..DisplayRow(5)
11227                ),
11228                (
11229                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11230                    DiffHunkStatus::Modified,
11231                    DisplayRow(9)..DisplayRow(11)
11232                ),
11233                (
11234                    "".to_string(),
11235                    DiffHunkStatus::Added,
11236                    DisplayRow(13)..DisplayRow(15)
11237                ),
11238                (
11239                    "".to_string(),
11240                    DiffHunkStatus::Added,
11241                    DisplayRow(19)..DisplayRow(20)
11242                ),
11243                (
11244                    "fn another2() {\n".to_string(),
11245                    DiffHunkStatus::Removed,
11246                    DisplayRow(23)..DisplayRow(23)
11247                ),
11248            ],
11249        );
11250        assert_eq!(all_hunks, all_expanded_hunks);
11251    });
11252}
11253
11254#[gpui::test]
11255async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11256    init_test(cx, |_| {});
11257
11258    let cols = 4;
11259    let rows = 10;
11260    let sample_text_1 = sample_text(rows, cols, 'a');
11261    assert_eq!(
11262        sample_text_1,
11263        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11264    );
11265    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11266    let sample_text_2 = sample_text(rows, cols, 'l');
11267    assert_eq!(
11268        sample_text_2,
11269        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11270    );
11271    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11272    let sample_text_3 = sample_text(rows, cols, 'v');
11273    assert_eq!(
11274        sample_text_3,
11275        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11276    );
11277    let modified_sample_text_3 =
11278        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11279    let buffer_1 = cx.new_model(|cx| {
11280        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11281        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11282        buffer
11283    });
11284    let buffer_2 = cx.new_model(|cx| {
11285        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11286        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11287        buffer
11288    });
11289    let buffer_3 = cx.new_model(|cx| {
11290        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11291        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11292        buffer
11293    });
11294
11295    let multi_buffer = cx.new_model(|cx| {
11296        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11297        multibuffer.push_excerpts(
11298            buffer_1.clone(),
11299            [
11300                ExcerptRange {
11301                    context: Point::new(0, 0)..Point::new(3, 0),
11302                    primary: None,
11303                },
11304                ExcerptRange {
11305                    context: Point::new(5, 0)..Point::new(7, 0),
11306                    primary: None,
11307                },
11308                ExcerptRange {
11309                    context: Point::new(9, 0)..Point::new(10, 4),
11310                    primary: None,
11311                },
11312            ],
11313            cx,
11314        );
11315        multibuffer.push_excerpts(
11316            buffer_2.clone(),
11317            [
11318                ExcerptRange {
11319                    context: Point::new(0, 0)..Point::new(3, 0),
11320                    primary: None,
11321                },
11322                ExcerptRange {
11323                    context: Point::new(5, 0)..Point::new(7, 0),
11324                    primary: None,
11325                },
11326                ExcerptRange {
11327                    context: Point::new(9, 0)..Point::new(10, 4),
11328                    primary: None,
11329                },
11330            ],
11331            cx,
11332        );
11333        multibuffer.push_excerpts(
11334            buffer_3.clone(),
11335            [
11336                ExcerptRange {
11337                    context: Point::new(0, 0)..Point::new(3, 0),
11338                    primary: None,
11339                },
11340                ExcerptRange {
11341                    context: Point::new(5, 0)..Point::new(7, 0),
11342                    primary: None,
11343                },
11344                ExcerptRange {
11345                    context: Point::new(9, 0)..Point::new(10, 4),
11346                    primary: None,
11347                },
11348            ],
11349            cx,
11350        );
11351        multibuffer
11352    });
11353
11354    let fs = FakeFs::new(cx.executor());
11355    fs.insert_tree(
11356        "/a",
11357        json!({
11358            "main.rs": modified_sample_text_1,
11359            "other.rs": modified_sample_text_2,
11360            "lib.rs": modified_sample_text_3,
11361        }),
11362    )
11363    .await;
11364
11365    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11366    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11367    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11368    let multi_buffer_editor = cx.new_view(|cx| {
11369        Editor::new(
11370            EditorMode::Full,
11371            multi_buffer,
11372            Some(project.clone()),
11373            true,
11374            cx,
11375        )
11376    });
11377    cx.executor().run_until_parked();
11378
11379    let expected_all_hunks = vec![
11380        (
11381            "bbbb\n".to_string(),
11382            DiffHunkStatus::Removed,
11383            DisplayRow(4)..DisplayRow(4),
11384        ),
11385        (
11386            "nnnn\n".to_string(),
11387            DiffHunkStatus::Modified,
11388            DisplayRow(21)..DisplayRow(22),
11389        ),
11390        (
11391            "".to_string(),
11392            DiffHunkStatus::Added,
11393            DisplayRow(41)..DisplayRow(42),
11394        ),
11395    ];
11396    let expected_all_hunks_shifted = vec![
11397        (
11398            "bbbb\n".to_string(),
11399            DiffHunkStatus::Removed,
11400            DisplayRow(5)..DisplayRow(5),
11401        ),
11402        (
11403            "nnnn\n".to_string(),
11404            DiffHunkStatus::Modified,
11405            DisplayRow(23)..DisplayRow(24),
11406        ),
11407        (
11408            "".to_string(),
11409            DiffHunkStatus::Added,
11410            DisplayRow(43)..DisplayRow(44),
11411        ),
11412    ];
11413
11414    multi_buffer_editor.update(cx, |editor, cx| {
11415        let snapshot = editor.snapshot(cx);
11416        let all_hunks = editor_hunks(editor, &snapshot, cx);
11417        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11418        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11419        assert_eq!(all_hunks, expected_all_hunks);
11420        assert_eq!(all_expanded_hunks, Vec::new());
11421    });
11422
11423    multi_buffer_editor.update(cx, |editor, cx| {
11424        editor.select_all(&SelectAll, cx);
11425        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11426    });
11427    cx.executor().run_until_parked();
11428    multi_buffer_editor.update(cx, |editor, cx| {
11429        let snapshot = editor.snapshot(cx);
11430        let all_hunks = editor_hunks(editor, &snapshot, cx);
11431        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11432        assert_eq!(
11433            expanded_hunks_background_highlights(editor, cx),
11434            vec![
11435                DisplayRow(23)..=DisplayRow(23),
11436                DisplayRow(43)..=DisplayRow(43)
11437            ],
11438        );
11439        assert_eq!(all_hunks, expected_all_hunks_shifted);
11440        assert_eq!(all_hunks, all_expanded_hunks);
11441    });
11442
11443    multi_buffer_editor.update(cx, |editor, cx| {
11444        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11445    });
11446    cx.executor().run_until_parked();
11447    multi_buffer_editor.update(cx, |editor, cx| {
11448        let snapshot = editor.snapshot(cx);
11449        let all_hunks = editor_hunks(editor, &snapshot, cx);
11450        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11451        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11452        assert_eq!(all_hunks, expected_all_hunks);
11453        assert_eq!(all_expanded_hunks, Vec::new());
11454    });
11455
11456    multi_buffer_editor.update(cx, |editor, cx| {
11457        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11458    });
11459    cx.executor().run_until_parked();
11460    multi_buffer_editor.update(cx, |editor, cx| {
11461        let snapshot = editor.snapshot(cx);
11462        let all_hunks = editor_hunks(editor, &snapshot, cx);
11463        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11464        assert_eq!(
11465            expanded_hunks_background_highlights(editor, cx),
11466            vec![
11467                DisplayRow(23)..=DisplayRow(23),
11468                DisplayRow(43)..=DisplayRow(43)
11469            ],
11470        );
11471        assert_eq!(all_hunks, expected_all_hunks_shifted);
11472        assert_eq!(all_hunks, all_expanded_hunks);
11473    });
11474
11475    multi_buffer_editor.update(cx, |editor, cx| {
11476        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11477    });
11478    cx.executor().run_until_parked();
11479    multi_buffer_editor.update(cx, |editor, cx| {
11480        let snapshot = editor.snapshot(cx);
11481        let all_hunks = editor_hunks(editor, &snapshot, cx);
11482        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11483        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11484        assert_eq!(all_hunks, expected_all_hunks);
11485        assert_eq!(all_expanded_hunks, Vec::new());
11486    });
11487}
11488
11489#[gpui::test]
11490async fn test_edits_around_toggled_additions(
11491    executor: BackgroundExecutor,
11492    cx: &mut gpui::TestAppContext,
11493) {
11494    init_test(cx, |_| {});
11495
11496    let mut cx = EditorTestContext::new(cx).await;
11497
11498    let diff_base = r#"
11499        use some::mod1;
11500        use some::mod2;
11501
11502        const A: u32 = 42;
11503
11504        fn main() {
11505            println!("hello");
11506
11507            println!("world");
11508        }
11509        "#
11510    .unindent();
11511    executor.run_until_parked();
11512    cx.set_state(
11513        &r#"
11514        use some::mod1;
11515        use some::mod2;
11516
11517        const A: u32 = 42;
11518        const B: u32 = 42;
11519        const C: u32 = 42;
11520        ˇ
11521
11522        fn main() {
11523            println!("hello");
11524
11525            println!("world");
11526        }
11527        "#
11528        .unindent(),
11529    );
11530
11531    cx.set_diff_base(Some(&diff_base));
11532    executor.run_until_parked();
11533    cx.update_editor(|editor, cx| {
11534        let snapshot = editor.snapshot(cx);
11535        let all_hunks = editor_hunks(editor, &snapshot, cx);
11536        assert_eq!(
11537            all_hunks,
11538            vec![(
11539                "".to_string(),
11540                DiffHunkStatus::Added,
11541                DisplayRow(4)..DisplayRow(7)
11542            )]
11543        );
11544    });
11545    cx.update_editor(|editor, cx| {
11546        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11547    });
11548    executor.run_until_parked();
11549    cx.assert_editor_state(
11550        &r#"
11551        use some::mod1;
11552        use some::mod2;
11553
11554        const A: u32 = 42;
11555        const B: u32 = 42;
11556        const C: u32 = 42;
11557        ˇ
11558
11559        fn main() {
11560            println!("hello");
11561
11562            println!("world");
11563        }
11564        "#
11565        .unindent(),
11566    );
11567    cx.update_editor(|editor, cx| {
11568        let snapshot = editor.snapshot(cx);
11569        let all_hunks = editor_hunks(editor, &snapshot, cx);
11570        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11571        assert_eq!(
11572            all_hunks,
11573            vec![(
11574                "".to_string(),
11575                DiffHunkStatus::Added,
11576                DisplayRow(4)..DisplayRow(7)
11577            )]
11578        );
11579        assert_eq!(
11580            expanded_hunks_background_highlights(editor, cx),
11581            vec![DisplayRow(4)..=DisplayRow(6)]
11582        );
11583        assert_eq!(all_hunks, all_expanded_hunks);
11584    });
11585
11586    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11587    executor.run_until_parked();
11588    cx.assert_editor_state(
11589        &r#"
11590        use some::mod1;
11591        use some::mod2;
11592
11593        const A: u32 = 42;
11594        const B: u32 = 42;
11595        const C: u32 = 42;
11596        const D: u32 = 42;
11597        ˇ
11598
11599        fn main() {
11600            println!("hello");
11601
11602            println!("world");
11603        }
11604        "#
11605        .unindent(),
11606    );
11607    cx.update_editor(|editor, cx| {
11608        let snapshot = editor.snapshot(cx);
11609        let all_hunks = editor_hunks(editor, &snapshot, cx);
11610        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11611        assert_eq!(
11612            all_hunks,
11613            vec![(
11614                "".to_string(),
11615                DiffHunkStatus::Added,
11616                DisplayRow(4)..DisplayRow(8)
11617            )]
11618        );
11619        assert_eq!(
11620            expanded_hunks_background_highlights(editor, cx),
11621            vec![DisplayRow(4)..=DisplayRow(6)],
11622            "Edited hunk should have one more line added"
11623        );
11624        assert_eq!(
11625            all_hunks, all_expanded_hunks,
11626            "Expanded hunk should also grow with the addition"
11627        );
11628    });
11629
11630    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11631    executor.run_until_parked();
11632    cx.assert_editor_state(
11633        &r#"
11634        use some::mod1;
11635        use some::mod2;
11636
11637        const A: u32 = 42;
11638        const B: u32 = 42;
11639        const C: u32 = 42;
11640        const D: u32 = 42;
11641        const E: u32 = 42;
11642        ˇ
11643
11644        fn main() {
11645            println!("hello");
11646
11647            println!("world");
11648        }
11649        "#
11650        .unindent(),
11651    );
11652    cx.update_editor(|editor, cx| {
11653        let snapshot = editor.snapshot(cx);
11654        let all_hunks = editor_hunks(editor, &snapshot, cx);
11655        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11656        assert_eq!(
11657            all_hunks,
11658            vec![(
11659                "".to_string(),
11660                DiffHunkStatus::Added,
11661                DisplayRow(4)..DisplayRow(9)
11662            )]
11663        );
11664        assert_eq!(
11665            expanded_hunks_background_highlights(editor, cx),
11666            vec![DisplayRow(4)..=DisplayRow(6)],
11667            "Edited hunk should have one more line added"
11668        );
11669        assert_eq!(all_hunks, all_expanded_hunks);
11670    });
11671
11672    cx.update_editor(|editor, cx| {
11673        editor.move_up(&MoveUp, cx);
11674        editor.delete_line(&DeleteLine, cx);
11675    });
11676    executor.run_until_parked();
11677    cx.assert_editor_state(
11678        &r#"
11679        use some::mod1;
11680        use some::mod2;
11681
11682        const A: u32 = 42;
11683        const B: u32 = 42;
11684        const C: u32 = 42;
11685        const D: u32 = 42;
11686        ˇ
11687
11688        fn main() {
11689            println!("hello");
11690
11691            println!("world");
11692        }
11693        "#
11694        .unindent(),
11695    );
11696    cx.update_editor(|editor, cx| {
11697        let snapshot = editor.snapshot(cx);
11698        let all_hunks = editor_hunks(editor, &snapshot, cx);
11699        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11700        assert_eq!(
11701            all_hunks,
11702            vec![(
11703                "".to_string(),
11704                DiffHunkStatus::Added,
11705                DisplayRow(4)..DisplayRow(8)
11706            )]
11707        );
11708        assert_eq!(
11709            expanded_hunks_background_highlights(editor, cx),
11710            vec![DisplayRow(4)..=DisplayRow(6)],
11711            "Deleting a line should shrint the hunk"
11712        );
11713        assert_eq!(
11714            all_hunks, all_expanded_hunks,
11715            "Expanded hunk should also shrink with the addition"
11716        );
11717    });
11718
11719    cx.update_editor(|editor, cx| {
11720        editor.move_up(&MoveUp, cx);
11721        editor.delete_line(&DeleteLine, cx);
11722        editor.move_up(&MoveUp, cx);
11723        editor.delete_line(&DeleteLine, cx);
11724        editor.move_up(&MoveUp, cx);
11725        editor.delete_line(&DeleteLine, cx);
11726    });
11727    executor.run_until_parked();
11728    cx.assert_editor_state(
11729        &r#"
11730        use some::mod1;
11731        use some::mod2;
11732
11733        const A: u32 = 42;
11734        ˇ
11735
11736        fn main() {
11737            println!("hello");
11738
11739            println!("world");
11740        }
11741        "#
11742        .unindent(),
11743    );
11744    cx.update_editor(|editor, cx| {
11745        let snapshot = editor.snapshot(cx);
11746        let all_hunks = editor_hunks(editor, &snapshot, cx);
11747        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11748        assert_eq!(
11749            all_hunks,
11750            vec![(
11751                "".to_string(),
11752                DiffHunkStatus::Added,
11753                DisplayRow(5)..DisplayRow(6)
11754            )]
11755        );
11756        assert_eq!(
11757            expanded_hunks_background_highlights(editor, cx),
11758            vec![DisplayRow(5)..=DisplayRow(5)]
11759        );
11760        assert_eq!(all_hunks, all_expanded_hunks);
11761    });
11762
11763    cx.update_editor(|editor, cx| {
11764        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11765        editor.delete_line(&DeleteLine, cx);
11766    });
11767    executor.run_until_parked();
11768    cx.assert_editor_state(
11769        &r#"
11770        ˇ
11771
11772        fn main() {
11773            println!("hello");
11774
11775            println!("world");
11776        }
11777        "#
11778        .unindent(),
11779    );
11780    cx.update_editor(|editor, cx| {
11781        let snapshot = editor.snapshot(cx);
11782        let all_hunks = editor_hunks(editor, &snapshot, cx);
11783        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11784        assert_eq!(
11785            all_hunks,
11786            vec![
11787                (
11788                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11789                    DiffHunkStatus::Removed,
11790                    DisplayRow(0)..DisplayRow(0)
11791                ),
11792                (
11793                    "const A: u32 = 42;\n".to_string(),
11794                    DiffHunkStatus::Removed,
11795                    DisplayRow(2)..DisplayRow(2)
11796                )
11797            ]
11798        );
11799        assert_eq!(
11800            expanded_hunks_background_highlights(editor, cx),
11801            Vec::new(),
11802            "Should close all stale expanded addition hunks"
11803        );
11804        assert_eq!(
11805            all_expanded_hunks,
11806            vec![(
11807                "const A: u32 = 42;\n".to_string(),
11808                DiffHunkStatus::Removed,
11809                DisplayRow(2)..DisplayRow(2)
11810            )],
11811            "Should open hunks that were adjacent to the stale addition one"
11812        );
11813    });
11814}
11815
11816#[gpui::test]
11817async fn test_edits_around_toggled_deletions(
11818    executor: BackgroundExecutor,
11819    cx: &mut gpui::TestAppContext,
11820) {
11821    init_test(cx, |_| {});
11822
11823    let mut cx = EditorTestContext::new(cx).await;
11824
11825    let diff_base = r#"
11826        use some::mod1;
11827        use some::mod2;
11828
11829        const A: u32 = 42;
11830        const B: u32 = 42;
11831        const C: u32 = 42;
11832
11833
11834        fn main() {
11835            println!("hello");
11836
11837            println!("world");
11838        }
11839        "#
11840    .unindent();
11841    executor.run_until_parked();
11842    cx.set_state(
11843        &r#"
11844        use some::mod1;
11845        use some::mod2;
11846
11847        ˇconst B: u32 = 42;
11848        const C: u32 = 42;
11849
11850
11851        fn main() {
11852            println!("hello");
11853
11854            println!("world");
11855        }
11856        "#
11857        .unindent(),
11858    );
11859
11860    cx.set_diff_base(Some(&diff_base));
11861    executor.run_until_parked();
11862    cx.update_editor(|editor, cx| {
11863        let snapshot = editor.snapshot(cx);
11864        let all_hunks = editor_hunks(editor, &snapshot, cx);
11865        assert_eq!(
11866            all_hunks,
11867            vec![(
11868                "const A: u32 = 42;\n".to_string(),
11869                DiffHunkStatus::Removed,
11870                DisplayRow(3)..DisplayRow(3)
11871            )]
11872        );
11873    });
11874    cx.update_editor(|editor, cx| {
11875        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11876    });
11877    executor.run_until_parked();
11878    cx.assert_editor_state(
11879        &r#"
11880        use some::mod1;
11881        use some::mod2;
11882
11883        ˇconst B: u32 = 42;
11884        const C: u32 = 42;
11885
11886
11887        fn main() {
11888            println!("hello");
11889
11890            println!("world");
11891        }
11892        "#
11893        .unindent(),
11894    );
11895    cx.update_editor(|editor, cx| {
11896        let snapshot = editor.snapshot(cx);
11897        let all_hunks = editor_hunks(editor, &snapshot, cx);
11898        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11899        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11900        assert_eq!(
11901            all_hunks,
11902            vec![(
11903                "const A: u32 = 42;\n".to_string(),
11904                DiffHunkStatus::Removed,
11905                DisplayRow(4)..DisplayRow(4)
11906            )]
11907        );
11908        assert_eq!(all_hunks, all_expanded_hunks);
11909    });
11910
11911    cx.update_editor(|editor, cx| {
11912        editor.delete_line(&DeleteLine, cx);
11913    });
11914    executor.run_until_parked();
11915    cx.assert_editor_state(
11916        &r#"
11917        use some::mod1;
11918        use some::mod2;
11919
11920        ˇconst C: u32 = 42;
11921
11922
11923        fn main() {
11924            println!("hello");
11925
11926            println!("world");
11927        }
11928        "#
11929        .unindent(),
11930    );
11931    cx.update_editor(|editor, cx| {
11932        let snapshot = editor.snapshot(cx);
11933        let all_hunks = editor_hunks(editor, &snapshot, cx);
11934        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11935        assert_eq!(
11936            expanded_hunks_background_highlights(editor, cx),
11937            Vec::new(),
11938            "Deleted hunks do not highlight current editor's background"
11939        );
11940        assert_eq!(
11941            all_hunks,
11942            vec![(
11943                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11944                DiffHunkStatus::Removed,
11945                DisplayRow(5)..DisplayRow(5)
11946            )]
11947        );
11948        assert_eq!(all_hunks, all_expanded_hunks);
11949    });
11950
11951    cx.update_editor(|editor, cx| {
11952        editor.delete_line(&DeleteLine, cx);
11953    });
11954    executor.run_until_parked();
11955    cx.assert_editor_state(
11956        &r#"
11957        use some::mod1;
11958        use some::mod2;
11959
11960        ˇ
11961
11962        fn main() {
11963            println!("hello");
11964
11965            println!("world");
11966        }
11967        "#
11968        .unindent(),
11969    );
11970    cx.update_editor(|editor, cx| {
11971        let snapshot = editor.snapshot(cx);
11972        let all_hunks = editor_hunks(editor, &snapshot, cx);
11973        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11974        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11975        assert_eq!(
11976            all_hunks,
11977            vec![(
11978                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11979                DiffHunkStatus::Removed,
11980                DisplayRow(6)..DisplayRow(6)
11981            )]
11982        );
11983        assert_eq!(all_hunks, all_expanded_hunks);
11984    });
11985
11986    cx.update_editor(|editor, cx| {
11987        editor.handle_input("replacement", cx);
11988    });
11989    executor.run_until_parked();
11990    cx.assert_editor_state(
11991        &r#"
11992        use some::mod1;
11993        use some::mod2;
11994
11995        replacementˇ
11996
11997        fn main() {
11998            println!("hello");
11999
12000            println!("world");
12001        }
12002        "#
12003        .unindent(),
12004    );
12005    cx.update_editor(|editor, cx| {
12006        let snapshot = editor.snapshot(cx);
12007        let all_hunks = editor_hunks(editor, &snapshot, cx);
12008        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12009        assert_eq!(
12010            all_hunks,
12011            vec![(
12012                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12013                DiffHunkStatus::Modified,
12014                DisplayRow(7)..DisplayRow(8)
12015            )]
12016        );
12017        assert_eq!(
12018            expanded_hunks_background_highlights(editor, cx),
12019            vec![DisplayRow(7)..=DisplayRow(7)],
12020            "Modified expanded hunks should display additions and highlight their background"
12021        );
12022        assert_eq!(all_hunks, all_expanded_hunks);
12023    });
12024}
12025
12026#[gpui::test]
12027async fn test_edits_around_toggled_modifications(
12028    executor: BackgroundExecutor,
12029    cx: &mut gpui::TestAppContext,
12030) {
12031    init_test(cx, |_| {});
12032
12033    let mut cx = EditorTestContext::new(cx).await;
12034
12035    let diff_base = r#"
12036        use some::mod1;
12037        use some::mod2;
12038
12039        const A: u32 = 42;
12040        const B: u32 = 42;
12041        const C: u32 = 42;
12042        const D: u32 = 42;
12043
12044
12045        fn main() {
12046            println!("hello");
12047
12048            println!("world");
12049        }"#
12050    .unindent();
12051    executor.run_until_parked();
12052    cx.set_state(
12053        &r#"
12054        use some::mod1;
12055        use some::mod2;
12056
12057        const A: u32 = 42;
12058        const B: u32 = 42;
12059        const C: u32 = 43ˇ
12060        const D: u32 = 42;
12061
12062
12063        fn main() {
12064            println!("hello");
12065
12066            println!("world");
12067        }"#
12068        .unindent(),
12069    );
12070
12071    cx.set_diff_base(Some(&diff_base));
12072    executor.run_until_parked();
12073    cx.update_editor(|editor, cx| {
12074        let snapshot = editor.snapshot(cx);
12075        let all_hunks = editor_hunks(editor, &snapshot, cx);
12076        assert_eq!(
12077            all_hunks,
12078            vec![(
12079                "const C: u32 = 42;\n".to_string(),
12080                DiffHunkStatus::Modified,
12081                DisplayRow(5)..DisplayRow(6)
12082            )]
12083        );
12084    });
12085    cx.update_editor(|editor, cx| {
12086        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12087    });
12088    executor.run_until_parked();
12089    cx.assert_editor_state(
12090        &r#"
12091        use some::mod1;
12092        use some::mod2;
12093
12094        const A: u32 = 42;
12095        const B: u32 = 42;
12096        const C: u32 = 43ˇ
12097        const D: u32 = 42;
12098
12099
12100        fn main() {
12101            println!("hello");
12102
12103            println!("world");
12104        }"#
12105        .unindent(),
12106    );
12107    cx.update_editor(|editor, cx| {
12108        let snapshot = editor.snapshot(cx);
12109        let all_hunks = editor_hunks(editor, &snapshot, cx);
12110        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12111        assert_eq!(
12112            expanded_hunks_background_highlights(editor, cx),
12113            vec![DisplayRow(6)..=DisplayRow(6)],
12114        );
12115        assert_eq!(
12116            all_hunks,
12117            vec![(
12118                "const C: u32 = 42;\n".to_string(),
12119                DiffHunkStatus::Modified,
12120                DisplayRow(6)..DisplayRow(7)
12121            )]
12122        );
12123        assert_eq!(all_hunks, all_expanded_hunks);
12124    });
12125
12126    cx.update_editor(|editor, cx| {
12127        editor.handle_input("\nnew_line\n", cx);
12128    });
12129    executor.run_until_parked();
12130    cx.assert_editor_state(
12131        &r#"
12132            use some::mod1;
12133            use some::mod2;
12134
12135            const A: u32 = 42;
12136            const B: u32 = 42;
12137            const C: u32 = 43
12138            new_line
12139            ˇ
12140            const D: u32 = 42;
12141
12142
12143            fn main() {
12144                println!("hello");
12145
12146                println!("world");
12147            }"#
12148        .unindent(),
12149    );
12150    cx.update_editor(|editor, cx| {
12151        let snapshot = editor.snapshot(cx);
12152        let all_hunks = editor_hunks(editor, &snapshot, cx);
12153        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12154        assert_eq!(
12155            expanded_hunks_background_highlights(editor, cx),
12156            vec![DisplayRow(6)..=DisplayRow(6)],
12157            "Modified hunk should grow highlighted lines on more text additions"
12158        );
12159        assert_eq!(
12160            all_hunks,
12161            vec![(
12162                "const C: u32 = 42;\n".to_string(),
12163                DiffHunkStatus::Modified,
12164                DisplayRow(6)..DisplayRow(9)
12165            )]
12166        );
12167        assert_eq!(all_hunks, all_expanded_hunks);
12168    });
12169
12170    cx.update_editor(|editor, cx| {
12171        editor.move_up(&MoveUp, cx);
12172        editor.move_up(&MoveUp, cx);
12173        editor.move_up(&MoveUp, cx);
12174        editor.delete_line(&DeleteLine, cx);
12175    });
12176    executor.run_until_parked();
12177    cx.assert_editor_state(
12178        &r#"
12179            use some::mod1;
12180            use some::mod2;
12181
12182            const A: u32 = 42;
12183            ˇconst C: u32 = 43
12184            new_line
12185
12186            const D: u32 = 42;
12187
12188
12189            fn main() {
12190                println!("hello");
12191
12192                println!("world");
12193            }"#
12194        .unindent(),
12195    );
12196    cx.update_editor(|editor, cx| {
12197        let snapshot = editor.snapshot(cx);
12198        let all_hunks = editor_hunks(editor, &snapshot, cx);
12199        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12200        assert_eq!(
12201            expanded_hunks_background_highlights(editor, cx),
12202            vec![DisplayRow(6)..=DisplayRow(8)],
12203        );
12204        assert_eq!(
12205            all_hunks,
12206            vec![(
12207                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12208                DiffHunkStatus::Modified,
12209                DisplayRow(6)..DisplayRow(9)
12210            )],
12211            "Modified hunk should grow deleted lines on text deletions above"
12212        );
12213        assert_eq!(all_hunks, all_expanded_hunks);
12214    });
12215
12216    cx.update_editor(|editor, cx| {
12217        editor.move_up(&MoveUp, cx);
12218        editor.handle_input("v", cx);
12219    });
12220    executor.run_until_parked();
12221    cx.assert_editor_state(
12222        &r#"
12223            use some::mod1;
12224            use some::mod2;
12225
12226            vˇconst A: u32 = 42;
12227            const C: u32 = 43
12228            new_line
12229
12230            const D: u32 = 42;
12231
12232
12233            fn main() {
12234                println!("hello");
12235
12236                println!("world");
12237            }"#
12238        .unindent(),
12239    );
12240    cx.update_editor(|editor, cx| {
12241        let snapshot = editor.snapshot(cx);
12242        let all_hunks = editor_hunks(editor, &snapshot, cx);
12243        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12244        assert_eq!(
12245            expanded_hunks_background_highlights(editor, cx),
12246            vec![DisplayRow(6)..=DisplayRow(9)],
12247            "Modified hunk should grow deleted lines on text modifications above"
12248        );
12249        assert_eq!(
12250            all_hunks,
12251            vec![(
12252                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12253                DiffHunkStatus::Modified,
12254                DisplayRow(6)..DisplayRow(10)
12255            )]
12256        );
12257        assert_eq!(all_hunks, all_expanded_hunks);
12258    });
12259
12260    cx.update_editor(|editor, cx| {
12261        editor.move_down(&MoveDown, cx);
12262        editor.move_down(&MoveDown, cx);
12263        editor.delete_line(&DeleteLine, cx)
12264    });
12265    executor.run_until_parked();
12266    cx.assert_editor_state(
12267        &r#"
12268            use some::mod1;
12269            use some::mod2;
12270
12271            vconst A: u32 = 42;
12272            const C: u32 = 43
12273            ˇ
12274            const D: u32 = 42;
12275
12276
12277            fn main() {
12278                println!("hello");
12279
12280                println!("world");
12281            }"#
12282        .unindent(),
12283    );
12284    cx.update_editor(|editor, cx| {
12285        let snapshot = editor.snapshot(cx);
12286        let all_hunks = editor_hunks(editor, &snapshot, cx);
12287        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12288        assert_eq!(
12289            expanded_hunks_background_highlights(editor, cx),
12290            vec![DisplayRow(6)..=DisplayRow(8)],
12291            "Modified hunk should grow shrink lines on modification lines removal"
12292        );
12293        assert_eq!(
12294            all_hunks,
12295            vec![(
12296                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12297                DiffHunkStatus::Modified,
12298                DisplayRow(6)..DisplayRow(9)
12299            )]
12300        );
12301        assert_eq!(all_hunks, all_expanded_hunks);
12302    });
12303
12304    cx.update_editor(|editor, cx| {
12305        editor.move_up(&MoveUp, cx);
12306        editor.move_up(&MoveUp, cx);
12307        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12308        editor.delete_line(&DeleteLine, cx)
12309    });
12310    executor.run_until_parked();
12311    cx.assert_editor_state(
12312        &r#"
12313            use some::mod1;
12314            use some::mod2;
12315
12316            ˇ
12317
12318            fn main() {
12319                println!("hello");
12320
12321                println!("world");
12322            }"#
12323        .unindent(),
12324    );
12325    cx.update_editor(|editor, cx| {
12326        let snapshot = editor.snapshot(cx);
12327        let all_hunks = editor_hunks(editor, &snapshot, cx);
12328        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12329        assert_eq!(
12330            expanded_hunks_background_highlights(editor, cx),
12331            Vec::new(),
12332            "Modified hunk should turn into a removed one on all modified lines removal"
12333        );
12334        assert_eq!(
12335            all_hunks,
12336            vec![(
12337                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12338                    .to_string(),
12339                DiffHunkStatus::Removed,
12340                DisplayRow(7)..DisplayRow(7)
12341            )]
12342        );
12343        assert_eq!(all_hunks, all_expanded_hunks);
12344    });
12345}
12346
12347#[gpui::test]
12348async fn test_multiple_expanded_hunks_merge(
12349    executor: BackgroundExecutor,
12350    cx: &mut gpui::TestAppContext,
12351) {
12352    init_test(cx, |_| {});
12353
12354    let mut cx = EditorTestContext::new(cx).await;
12355
12356    let diff_base = r#"
12357        use some::mod1;
12358        use some::mod2;
12359
12360        const A: u32 = 42;
12361        const B: u32 = 42;
12362        const C: u32 = 42;
12363        const D: u32 = 42;
12364
12365
12366        fn main() {
12367            println!("hello");
12368
12369            println!("world");
12370        }"#
12371    .unindent();
12372    executor.run_until_parked();
12373    cx.set_state(
12374        &r#"
12375        use some::mod1;
12376        use some::mod2;
12377
12378        const A: u32 = 42;
12379        const B: u32 = 42;
12380        const C: u32 = 43ˇ
12381        const D: u32 = 42;
12382
12383
12384        fn main() {
12385            println!("hello");
12386
12387            println!("world");
12388        }"#
12389        .unindent(),
12390    );
12391
12392    cx.set_diff_base(Some(&diff_base));
12393    executor.run_until_parked();
12394    cx.update_editor(|editor, cx| {
12395        let snapshot = editor.snapshot(cx);
12396        let all_hunks = editor_hunks(editor, &snapshot, cx);
12397        assert_eq!(
12398            all_hunks,
12399            vec![(
12400                "const C: u32 = 42;\n".to_string(),
12401                DiffHunkStatus::Modified,
12402                DisplayRow(5)..DisplayRow(6)
12403            )]
12404        );
12405    });
12406    cx.update_editor(|editor, cx| {
12407        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12408    });
12409    executor.run_until_parked();
12410    cx.assert_editor_state(
12411        &r#"
12412        use some::mod1;
12413        use some::mod2;
12414
12415        const A: u32 = 42;
12416        const B: u32 = 42;
12417        const C: u32 = 43ˇ
12418        const D: u32 = 42;
12419
12420
12421        fn main() {
12422            println!("hello");
12423
12424            println!("world");
12425        }"#
12426        .unindent(),
12427    );
12428    cx.update_editor(|editor, cx| {
12429        let snapshot = editor.snapshot(cx);
12430        let all_hunks = editor_hunks(editor, &snapshot, cx);
12431        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12432        assert_eq!(
12433            expanded_hunks_background_highlights(editor, cx),
12434            vec![DisplayRow(6)..=DisplayRow(6)],
12435        );
12436        assert_eq!(
12437            all_hunks,
12438            vec![(
12439                "const C: u32 = 42;\n".to_string(),
12440                DiffHunkStatus::Modified,
12441                DisplayRow(6)..DisplayRow(7)
12442            )]
12443        );
12444        assert_eq!(all_hunks, all_expanded_hunks);
12445    });
12446
12447    cx.update_editor(|editor, cx| {
12448        editor.handle_input("\nnew_line\n", cx);
12449    });
12450    executor.run_until_parked();
12451    cx.assert_editor_state(
12452        &r#"
12453            use some::mod1;
12454            use some::mod2;
12455
12456            const A: u32 = 42;
12457            const B: u32 = 42;
12458            const C: u32 = 43
12459            new_line
12460            ˇ
12461            const D: u32 = 42;
12462
12463
12464            fn main() {
12465                println!("hello");
12466
12467                println!("world");
12468            }"#
12469        .unindent(),
12470    );
12471}
12472
12473async fn setup_indent_guides_editor(
12474    text: &str,
12475    cx: &mut gpui::TestAppContext,
12476) -> (BufferId, EditorTestContext) {
12477    init_test(cx, |_| {});
12478
12479    let mut cx = EditorTestContext::new(cx).await;
12480
12481    let buffer_id = cx.update_editor(|editor, cx| {
12482        editor.set_text(text, cx);
12483        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12484        let buffer_id = buffer_ids[0];
12485        buffer_id
12486    });
12487
12488    (buffer_id, cx)
12489}
12490
12491fn assert_indent_guides(
12492    range: Range<u32>,
12493    expected: Vec<IndentGuide>,
12494    active_indices: Option<Vec<usize>>,
12495    cx: &mut EditorTestContext,
12496) {
12497    let indent_guides = cx.update_editor(|editor, cx| {
12498        let snapshot = editor.snapshot(cx).display_snapshot;
12499        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12500            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12501            true,
12502            &snapshot,
12503            cx,
12504        );
12505
12506        indent_guides.sort_by(|a, b| {
12507            a.depth.cmp(&b.depth).then(
12508                a.start_row
12509                    .cmp(&b.start_row)
12510                    .then(a.end_row.cmp(&b.end_row)),
12511            )
12512        });
12513        indent_guides
12514    });
12515
12516    if let Some(expected) = active_indices {
12517        let active_indices = cx.update_editor(|editor, cx| {
12518            let snapshot = editor.snapshot(cx).display_snapshot;
12519            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12520        });
12521
12522        assert_eq!(
12523            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12524            expected,
12525            "Active indent guide indices do not match"
12526        );
12527    }
12528
12529    let expected: Vec<_> = expected
12530        .into_iter()
12531        .map(|guide| MultiBufferIndentGuide {
12532            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12533            buffer: guide,
12534        })
12535        .collect();
12536
12537    assert_eq!(indent_guides, expected, "Indent guides do not match");
12538}
12539
12540fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12541    IndentGuide {
12542        buffer_id,
12543        start_row,
12544        end_row,
12545        depth,
12546        tab_size: 4,
12547        settings: IndentGuideSettings {
12548            enabled: true,
12549            line_width: 1,
12550            active_line_width: 1,
12551            ..Default::default()
12552        },
12553    }
12554}
12555
12556#[gpui::test]
12557async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12558    let (buffer_id, mut cx) = setup_indent_guides_editor(
12559        &"
12560    fn main() {
12561        let a = 1;
12562    }"
12563        .unindent(),
12564        cx,
12565    )
12566    .await;
12567
12568    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12569}
12570
12571#[gpui::test]
12572async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12573    let (buffer_id, mut cx) = setup_indent_guides_editor(
12574        &"
12575    fn main() {
12576        let a = 1;
12577        let b = 2;
12578    }"
12579        .unindent(),
12580        cx,
12581    )
12582    .await;
12583
12584    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12585}
12586
12587#[gpui::test]
12588async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12589    let (buffer_id, mut cx) = setup_indent_guides_editor(
12590        &"
12591    fn main() {
12592        let a = 1;
12593        if a == 3 {
12594            let b = 2;
12595        } else {
12596            let c = 3;
12597        }
12598    }"
12599        .unindent(),
12600        cx,
12601    )
12602    .await;
12603
12604    assert_indent_guides(
12605        0..8,
12606        vec![
12607            indent_guide(buffer_id, 1, 6, 0),
12608            indent_guide(buffer_id, 3, 3, 1),
12609            indent_guide(buffer_id, 5, 5, 1),
12610        ],
12611        None,
12612        &mut cx,
12613    );
12614}
12615
12616#[gpui::test]
12617async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12618    let (buffer_id, mut cx) = setup_indent_guides_editor(
12619        &"
12620    fn main() {
12621        let a = 1;
12622            let b = 2;
12623        let c = 3;
12624    }"
12625        .unindent(),
12626        cx,
12627    )
12628    .await;
12629
12630    assert_indent_guides(
12631        0..5,
12632        vec![
12633            indent_guide(buffer_id, 1, 3, 0),
12634            indent_guide(buffer_id, 2, 2, 1),
12635        ],
12636        None,
12637        &mut cx,
12638    );
12639}
12640
12641#[gpui::test]
12642async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12643    let (buffer_id, mut cx) = setup_indent_guides_editor(
12644        &"
12645        fn main() {
12646            let a = 1;
12647
12648            let c = 3;
12649        }"
12650        .unindent(),
12651        cx,
12652    )
12653    .await;
12654
12655    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12656}
12657
12658#[gpui::test]
12659async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12660    let (buffer_id, mut cx) = setup_indent_guides_editor(
12661        &"
12662        fn main() {
12663            let a = 1;
12664
12665            let c = 3;
12666
12667            if a == 3 {
12668                let b = 2;
12669            } else {
12670                let c = 3;
12671            }
12672        }"
12673        .unindent(),
12674        cx,
12675    )
12676    .await;
12677
12678    assert_indent_guides(
12679        0..11,
12680        vec![
12681            indent_guide(buffer_id, 1, 9, 0),
12682            indent_guide(buffer_id, 6, 6, 1),
12683            indent_guide(buffer_id, 8, 8, 1),
12684        ],
12685        None,
12686        &mut cx,
12687    );
12688}
12689
12690#[gpui::test]
12691async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12692    let (buffer_id, mut cx) = setup_indent_guides_editor(
12693        &"
12694        fn main() {
12695            let a = 1;
12696
12697            let c = 3;
12698
12699            if a == 3 {
12700                let b = 2;
12701            } else {
12702                let c = 3;
12703            }
12704        }"
12705        .unindent(),
12706        cx,
12707    )
12708    .await;
12709
12710    assert_indent_guides(
12711        1..11,
12712        vec![
12713            indent_guide(buffer_id, 1, 9, 0),
12714            indent_guide(buffer_id, 6, 6, 1),
12715            indent_guide(buffer_id, 8, 8, 1),
12716        ],
12717        None,
12718        &mut cx,
12719    );
12720}
12721
12722#[gpui::test]
12723async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12724    let (buffer_id, mut cx) = setup_indent_guides_editor(
12725        &"
12726        fn main() {
12727            let a = 1;
12728
12729            let c = 3;
12730
12731            if a == 3 {
12732                let b = 2;
12733            } else {
12734                let c = 3;
12735            }
12736        }"
12737        .unindent(),
12738        cx,
12739    )
12740    .await;
12741
12742    assert_indent_guides(
12743        1..10,
12744        vec![
12745            indent_guide(buffer_id, 1, 9, 0),
12746            indent_guide(buffer_id, 6, 6, 1),
12747            indent_guide(buffer_id, 8, 8, 1),
12748        ],
12749        None,
12750        &mut cx,
12751    );
12752}
12753
12754#[gpui::test]
12755async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12756    let (buffer_id, mut cx) = setup_indent_guides_editor(
12757        &"
12758        block1
12759            block2
12760                block3
12761                    block4
12762            block2
12763        block1
12764        block1"
12765            .unindent(),
12766        cx,
12767    )
12768    .await;
12769
12770    assert_indent_guides(
12771        1..10,
12772        vec![
12773            indent_guide(buffer_id, 1, 4, 0),
12774            indent_guide(buffer_id, 2, 3, 1),
12775            indent_guide(buffer_id, 3, 3, 2),
12776        ],
12777        None,
12778        &mut cx,
12779    );
12780}
12781
12782#[gpui::test]
12783async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12784    let (buffer_id, mut cx) = setup_indent_guides_editor(
12785        &"
12786        block1
12787            block2
12788                block3
12789
12790        block1
12791        block1"
12792            .unindent(),
12793        cx,
12794    )
12795    .await;
12796
12797    assert_indent_guides(
12798        0..6,
12799        vec![
12800            indent_guide(buffer_id, 1, 2, 0),
12801            indent_guide(buffer_id, 2, 2, 1),
12802        ],
12803        None,
12804        &mut cx,
12805    );
12806}
12807
12808#[gpui::test]
12809async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12810    let (buffer_id, mut cx) = setup_indent_guides_editor(
12811        &"
12812        block1
12813
12814
12815
12816            block2
12817        "
12818        .unindent(),
12819        cx,
12820    )
12821    .await;
12822
12823    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12824}
12825
12826#[gpui::test]
12827async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12828    let (buffer_id, mut cx) = setup_indent_guides_editor(
12829        &"
12830        def a:
12831        \tb = 3
12832        \tif True:
12833        \t\tc = 4
12834        \t\td = 5
12835        \tprint(b)
12836        "
12837        .unindent(),
12838        cx,
12839    )
12840    .await;
12841
12842    assert_indent_guides(
12843        0..6,
12844        vec![
12845            indent_guide(buffer_id, 1, 6, 0),
12846            indent_guide(buffer_id, 3, 4, 1),
12847        ],
12848        None,
12849        &mut cx,
12850    );
12851}
12852
12853#[gpui::test]
12854async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12855    let (buffer_id, mut cx) = setup_indent_guides_editor(
12856        &"
12857    fn main() {
12858        let a = 1;
12859    }"
12860        .unindent(),
12861        cx,
12862    )
12863    .await;
12864
12865    cx.update_editor(|editor, cx| {
12866        editor.change_selections(None, cx, |s| {
12867            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12868        });
12869    });
12870
12871    assert_indent_guides(
12872        0..3,
12873        vec![indent_guide(buffer_id, 1, 1, 0)],
12874        Some(vec![0]),
12875        &mut cx,
12876    );
12877}
12878
12879#[gpui::test]
12880async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12881    let (buffer_id, mut cx) = setup_indent_guides_editor(
12882        &"
12883    fn main() {
12884        if 1 == 2 {
12885            let a = 1;
12886        }
12887    }"
12888        .unindent(),
12889        cx,
12890    )
12891    .await;
12892
12893    cx.update_editor(|editor, cx| {
12894        editor.change_selections(None, cx, |s| {
12895            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12896        });
12897    });
12898
12899    assert_indent_guides(
12900        0..4,
12901        vec![
12902            indent_guide(buffer_id, 1, 3, 0),
12903            indent_guide(buffer_id, 2, 2, 1),
12904        ],
12905        Some(vec![1]),
12906        &mut cx,
12907    );
12908
12909    cx.update_editor(|editor, cx| {
12910        editor.change_selections(None, cx, |s| {
12911            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12912        });
12913    });
12914
12915    assert_indent_guides(
12916        0..4,
12917        vec![
12918            indent_guide(buffer_id, 1, 3, 0),
12919            indent_guide(buffer_id, 2, 2, 1),
12920        ],
12921        Some(vec![1]),
12922        &mut cx,
12923    );
12924
12925    cx.update_editor(|editor, cx| {
12926        editor.change_selections(None, cx, |s| {
12927            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12928        });
12929    });
12930
12931    assert_indent_guides(
12932        0..4,
12933        vec![
12934            indent_guide(buffer_id, 1, 3, 0),
12935            indent_guide(buffer_id, 2, 2, 1),
12936        ],
12937        Some(vec![0]),
12938        &mut cx,
12939    );
12940}
12941
12942#[gpui::test]
12943async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12944    let (buffer_id, mut cx) = setup_indent_guides_editor(
12945        &"
12946    fn main() {
12947        let a = 1;
12948
12949        let b = 2;
12950    }"
12951        .unindent(),
12952        cx,
12953    )
12954    .await;
12955
12956    cx.update_editor(|editor, cx| {
12957        editor.change_selections(None, cx, |s| {
12958            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12959        });
12960    });
12961
12962    assert_indent_guides(
12963        0..5,
12964        vec![indent_guide(buffer_id, 1, 3, 0)],
12965        Some(vec![0]),
12966        &mut cx,
12967    );
12968}
12969
12970#[gpui::test]
12971async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12972    let (buffer_id, mut cx) = setup_indent_guides_editor(
12973        &"
12974    def m:
12975        a = 1
12976        pass"
12977            .unindent(),
12978        cx,
12979    )
12980    .await;
12981
12982    cx.update_editor(|editor, cx| {
12983        editor.change_selections(None, cx, |s| {
12984            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12985        });
12986    });
12987
12988    assert_indent_guides(
12989        0..3,
12990        vec![indent_guide(buffer_id, 1, 2, 0)],
12991        Some(vec![0]),
12992        &mut cx,
12993    );
12994}
12995
12996#[gpui::test]
12997fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12998    init_test(cx, |_| {});
12999
13000    let editor = cx.add_window(|cx| {
13001        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13002        build_editor(buffer, cx)
13003    });
13004
13005    let render_args = Arc::new(Mutex::new(None));
13006    let snapshot = editor
13007        .update(cx, |editor, cx| {
13008            let snapshot = editor.buffer().read(cx).snapshot(cx);
13009            let range =
13010                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13011
13012            struct RenderArgs {
13013                row: MultiBufferRow,
13014                folded: bool,
13015                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13016            }
13017
13018            let crease = Crease::new(
13019                range,
13020                FoldPlaceholder::test(),
13021                {
13022                    let toggle_callback = render_args.clone();
13023                    move |row, folded, callback, _cx| {
13024                        *toggle_callback.lock() = Some(RenderArgs {
13025                            row,
13026                            folded,
13027                            callback,
13028                        });
13029                        div()
13030                    }
13031                },
13032                |_row, _folded, _cx| div(),
13033            );
13034
13035            editor.insert_creases(Some(crease), cx);
13036            let snapshot = editor.snapshot(cx);
13037            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13038            snapshot
13039        })
13040        .unwrap();
13041
13042    let render_args = render_args.lock().take().unwrap();
13043    assert_eq!(render_args.row, MultiBufferRow(1));
13044    assert_eq!(render_args.folded, false);
13045    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13046
13047    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13048        .unwrap();
13049    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13050    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13051
13052    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13053        .unwrap();
13054    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13055    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13056}
13057
13058fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13059    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13060    point..point
13061}
13062
13063fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13064    let (text, ranges) = marked_text_ranges(marked_text, true);
13065    assert_eq!(view.text(cx), text);
13066    assert_eq!(
13067        view.selections.ranges(cx),
13068        ranges,
13069        "Assert selections are {}",
13070        marked_text
13071    );
13072}
13073
13074pub fn handle_signature_help_request(
13075    cx: &mut EditorLspTestContext,
13076    mocked_response: lsp::SignatureHelp,
13077) -> impl Future<Output = ()> {
13078    let mut request =
13079        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13080            let mocked_response = mocked_response.clone();
13081            async move { Ok(Some(mocked_response)) }
13082        });
13083
13084    async move {
13085        request.next().await;
13086    }
13087}
13088
13089/// Handle completion request passing a marked string specifying where the completion
13090/// should be triggered from using '|' character, what range should be replaced, and what completions
13091/// should be returned using '<' and '>' to delimit the range
13092pub fn handle_completion_request(
13093    cx: &mut EditorLspTestContext,
13094    marked_string: &str,
13095    completions: Vec<&'static str>,
13096    counter: Arc<AtomicUsize>,
13097) -> impl Future<Output = ()> {
13098    let complete_from_marker: TextRangeMarker = '|'.into();
13099    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13100    let (_, mut marked_ranges) = marked_text_ranges_by(
13101        marked_string,
13102        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13103    );
13104
13105    let complete_from_position =
13106        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13107    let replace_range =
13108        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13109
13110    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13111        let completions = completions.clone();
13112        counter.fetch_add(1, atomic::Ordering::Release);
13113        async move {
13114            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13115            assert_eq!(
13116                params.text_document_position.position,
13117                complete_from_position
13118            );
13119            Ok(Some(lsp::CompletionResponse::Array(
13120                completions
13121                    .iter()
13122                    .map(|completion_text| lsp::CompletionItem {
13123                        label: completion_text.to_string(),
13124                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13125                            range: replace_range,
13126                            new_text: completion_text.to_string(),
13127                        })),
13128                        ..Default::default()
13129                    })
13130                    .collect(),
13131            )))
13132        }
13133    });
13134
13135    async move {
13136        request.next().await;
13137    }
13138}
13139
13140fn handle_resolve_completion_request(
13141    cx: &mut EditorLspTestContext,
13142    edits: Option<Vec<(&'static str, &'static str)>>,
13143) -> impl Future<Output = ()> {
13144    let edits = edits.map(|edits| {
13145        edits
13146            .iter()
13147            .map(|(marked_string, new_text)| {
13148                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13149                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13150                lsp::TextEdit::new(replace_range, new_text.to_string())
13151            })
13152            .collect::<Vec<_>>()
13153    });
13154
13155    let mut request =
13156        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13157            let edits = edits.clone();
13158            async move {
13159                Ok(lsp::CompletionItem {
13160                    additional_text_edits: edits,
13161                    ..Default::default()
13162                })
13163            }
13164        });
13165
13166    async move {
13167        request.next().await;
13168    }
13169}
13170
13171pub(crate) fn update_test_language_settings(
13172    cx: &mut TestAppContext,
13173    f: impl Fn(&mut AllLanguageSettingsContent),
13174) {
13175    _ = cx.update(|cx| {
13176        SettingsStore::update_global(cx, |store, cx| {
13177            store.update_user_settings::<AllLanguageSettings>(cx, f);
13178        });
13179    });
13180}
13181
13182pub(crate) fn update_test_project_settings(
13183    cx: &mut TestAppContext,
13184    f: impl Fn(&mut ProjectSettings),
13185) {
13186    _ = cx.update(|cx| {
13187        SettingsStore::update_global(cx, |store, cx| {
13188            store.update_user_settings::<ProjectSettings>(cx, f);
13189        });
13190    });
13191}
13192
13193pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13194    _ = cx.update(|cx| {
13195        assets::Assets.load_test_fonts(cx);
13196        let store = SettingsStore::test(cx);
13197        cx.set_global(store);
13198        theme::init(theme::LoadThemes::JustBase, cx);
13199        release_channel::init(SemanticVersion::default(), cx);
13200        client::init_settings(cx);
13201        language::init(cx);
13202        Project::init_settings(cx);
13203        workspace::init_settings(cx);
13204        crate::init(cx);
13205    });
13206
13207    update_test_language_settings(cx, f);
13208}
13209
13210pub(crate) fn rust_lang() -> Arc<Language> {
13211    Arc::new(Language::new(
13212        LanguageConfig {
13213            name: "Rust".into(),
13214            matcher: LanguageMatcher {
13215                path_suffixes: vec!["rs".to_string()],
13216                ..Default::default()
13217            },
13218            ..Default::default()
13219        },
13220        Some(tree_sitter_rust::language()),
13221    ))
13222}
13223
13224#[track_caller]
13225fn assert_hunk_revert(
13226    not_reverted_text_with_selections: &str,
13227    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13228    expected_reverted_text_with_selections: &str,
13229    base_text: &str,
13230    cx: &mut EditorLspTestContext,
13231) {
13232    cx.set_state(not_reverted_text_with_selections);
13233    cx.update_editor(|editor, cx| {
13234        editor
13235            .buffer()
13236            .read(cx)
13237            .as_singleton()
13238            .unwrap()
13239            .update(cx, |buffer, cx| {
13240                buffer.set_diff_base(Some(base_text.into()), cx);
13241            });
13242    });
13243    cx.executor().run_until_parked();
13244
13245    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13246        let snapshot = editor.buffer().read(cx).snapshot(cx);
13247        let reverted_hunk_statuses = snapshot
13248            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13249            .map(|hunk| hunk_status(&hunk))
13250            .collect::<Vec<_>>();
13251
13252        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13253        reverted_hunk_statuses
13254    });
13255    cx.executor().run_until_parked();
13256    cx.assert_editor_state(expected_reverted_text_with_selections);
13257    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13258}