editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use project::{buffer_store::BufferChangeSet, FakeFs};
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic::AtomicUsize;
   35use std::sync::atomic::{self, AtomicBool};
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use test::editor_lsp_test_context::rust_lang;
   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 group_interval = Duration::from_millis(1);
  174    let buffer = cx.new_model(|cx| {
  175        let mut buf = language::Buffer::local("123456", cx);
  176        buf.set_group_interval(group_interval);
  177        buf
  178    });
  179    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  180    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  181
  182    _ = editor.update(cx, |editor, cx| {
  183        editor.start_transaction_at(now, cx);
  184        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  185
  186        editor.insert("cd", cx);
  187        editor.end_transaction_at(now, cx);
  188        assert_eq!(editor.text(cx), "12cd56");
  189        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  190
  191        editor.start_transaction_at(now, cx);
  192        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  193        editor.insert("e", cx);
  194        editor.end_transaction_at(now, cx);
  195        assert_eq!(editor.text(cx), "12cde6");
  196        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  197
  198        now += group_interval + Duration::from_millis(1);
  199        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  200
  201        // Simulate an edit in another editor
  202        buffer.update(cx, |buffer, cx| {
  203            buffer.start_transaction_at(now, cx);
  204            buffer.edit([(0..1, "a")], None, cx);
  205            buffer.edit([(1..1, "b")], None, cx);
  206            buffer.end_transaction_at(now, cx);
  207        });
  208
  209        assert_eq!(editor.text(cx), "ab2cde6");
  210        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  211
  212        // Last transaction happened past the group interval in a different editor.
  213        // Undo it individually and don't restore selections.
  214        editor.undo(&Undo, cx);
  215        assert_eq!(editor.text(cx), "12cde6");
  216        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  217
  218        // First two transactions happened within the group interval in this editor.
  219        // Undo them together and restore selections.
  220        editor.undo(&Undo, cx);
  221        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  222        assert_eq!(editor.text(cx), "123456");
  223        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  224
  225        // Redo the first two transactions together.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "12cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  229
  230        // Redo the last transaction on its own.
  231        editor.redo(&Redo, cx);
  232        assert_eq!(editor.text(cx), "ab2cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  234
  235        // Test empty transactions.
  236        editor.start_transaction_at(now, cx);
  237        editor.end_transaction_at(now, cx);
  238        editor.undo(&Undo, cx);
  239        assert_eq!(editor.text(cx), "12cde6");
  240    });
  241}
  242
  243#[gpui::test]
  244fn test_ime_composition(cx: &mut TestAppContext) {
  245    init_test(cx, |_| {});
  246
  247    let buffer = cx.new_model(|cx| {
  248        let mut buffer = language::Buffer::local("abcde", cx);
  249        // Ensure automatic grouping doesn't occur.
  250        buffer.set_group_interval(Duration::ZERO);
  251        buffer
  252    });
  253
  254    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  255    cx.add_window(|cx| {
  256        let mut editor = build_editor(buffer.clone(), cx);
  257
  258        // Start a new IME composition.
  259        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  260        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  261        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  262        assert_eq!(editor.text(cx), "äbcde");
  263        assert_eq!(
  264            editor.marked_text_ranges(cx),
  265            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  266        );
  267
  268        // Finalize IME composition.
  269        editor.replace_text_in_range(None, "ā", cx);
  270        assert_eq!(editor.text(cx), "ābcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272
  273        // IME composition edits are grouped and are undone/redone at once.
  274        editor.undo(&Default::default(), cx);
  275        assert_eq!(editor.text(cx), "abcde");
  276        assert_eq!(editor.marked_text_ranges(cx), None);
  277        editor.redo(&Default::default(), cx);
  278        assert_eq!(editor.text(cx), "ābcde");
  279        assert_eq!(editor.marked_text_ranges(cx), None);
  280
  281        // Start a new IME composition.
  282        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  283        assert_eq!(
  284            editor.marked_text_ranges(cx),
  285            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  286        );
  287
  288        // Undoing during an IME composition cancels it.
  289        editor.undo(&Default::default(), cx);
  290        assert_eq!(editor.text(cx), "ābcde");
  291        assert_eq!(editor.marked_text_ranges(cx), None);
  292
  293        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  294        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  295        assert_eq!(editor.text(cx), "ābcdè");
  296        assert_eq!(
  297            editor.marked_text_ranges(cx),
  298            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  299        );
  300
  301        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  302        editor.replace_text_in_range(Some(4..999), "ę", cx);
  303        assert_eq!(editor.text(cx), "ābcdę");
  304        assert_eq!(editor.marked_text_ranges(cx), None);
  305
  306        // Start a new IME composition with multiple cursors.
  307        editor.change_selections(None, cx, |s| {
  308            s.select_ranges([
  309                OffsetUtf16(1)..OffsetUtf16(1),
  310                OffsetUtf16(3)..OffsetUtf16(3),
  311                OffsetUtf16(5)..OffsetUtf16(5),
  312            ])
  313        });
  314        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  315        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  316        assert_eq!(
  317            editor.marked_text_ranges(cx),
  318            Some(vec![
  319                OffsetUtf16(0)..OffsetUtf16(3),
  320                OffsetUtf16(4)..OffsetUtf16(7),
  321                OffsetUtf16(8)..OffsetUtf16(11)
  322            ])
  323        );
  324
  325        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  326        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  327        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  328        assert_eq!(
  329            editor.marked_text_ranges(cx),
  330            Some(vec![
  331                OffsetUtf16(1)..OffsetUtf16(2),
  332                OffsetUtf16(5)..OffsetUtf16(6),
  333                OffsetUtf16(9)..OffsetUtf16(10)
  334            ])
  335        );
  336
  337        // Finalize IME composition with multiple cursors.
  338        editor.replace_text_in_range(Some(9..10), "2", cx);
  339        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  340        assert_eq!(editor.marked_text_ranges(cx), None);
  341
  342        editor
  343    });
  344}
  345
  346#[gpui::test]
  347fn test_selection_with_mouse(cx: &mut TestAppContext) {
  348    init_test(cx, |_| {});
  349
  350    let editor = cx.add_window(|cx| {
  351        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  352        build_editor(buffer, cx)
  353    });
  354
  355    _ = editor.update(cx, |view, cx| {
  356        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  357    });
  358    assert_eq!(
  359        editor
  360            .update(cx, |view, cx| view.selections.display_ranges(cx))
  361            .unwrap(),
  362        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  363    );
  364
  365    _ = editor.update(cx, |view, cx| {
  366        view.update_selection(
  367            DisplayPoint::new(DisplayRow(3), 3),
  368            0,
  369            gpui::Point::<f32>::default(),
  370            cx,
  371        );
  372    });
  373
  374    assert_eq!(
  375        editor
  376            .update(cx, |view, cx| view.selections.display_ranges(cx))
  377            .unwrap(),
  378        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  379    );
  380
  381    _ = editor.update(cx, |view, cx| {
  382        view.update_selection(
  383            DisplayPoint::new(DisplayRow(1), 1),
  384            0,
  385            gpui::Point::<f32>::default(),
  386            cx,
  387        );
  388    });
  389
  390    assert_eq!(
  391        editor
  392            .update(cx, |view, cx| view.selections.display_ranges(cx))
  393            .unwrap(),
  394        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  395    );
  396
  397    _ = editor.update(cx, |view, cx| {
  398        view.end_selection(cx);
  399        view.update_selection(
  400            DisplayPoint::new(DisplayRow(3), 3),
  401            0,
  402            gpui::Point::<f32>::default(),
  403            cx,
  404        );
  405    });
  406
  407    assert_eq!(
  408        editor
  409            .update(cx, |view, cx| view.selections.display_ranges(cx))
  410            .unwrap(),
  411        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  412    );
  413
  414    _ = editor.update(cx, |view, cx| {
  415        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  416        view.update_selection(
  417            DisplayPoint::new(DisplayRow(0), 0),
  418            0,
  419            gpui::Point::<f32>::default(),
  420            cx,
  421        );
  422    });
  423
  424    assert_eq!(
  425        editor
  426            .update(cx, |view, cx| view.selections.display_ranges(cx))
  427            .unwrap(),
  428        [
  429            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  430            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  431        ]
  432    );
  433
  434    _ = editor.update(cx, |view, cx| {
  435        view.end_selection(cx);
  436    });
  437
  438    assert_eq!(
  439        editor
  440            .update(cx, |view, cx| view.selections.display_ranges(cx))
  441            .unwrap(),
  442        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  443    );
  444}
  445
  446#[gpui::test]
  447fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  448    init_test(cx, |_| {});
  449
  450    let editor = cx.add_window(|cx| {
  451        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  452        build_editor(buffer, cx)
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.end_selection(cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  465    });
  466
  467    _ = editor.update(cx, |view, cx| {
  468        view.end_selection(cx);
  469    });
  470
  471    assert_eq!(
  472        editor
  473            .update(cx, |view, cx| view.selections.display_ranges(cx))
  474            .unwrap(),
  475        [
  476            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  477            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  478        ]
  479    );
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  483    });
  484
  485    _ = editor.update(cx, |view, cx| {
  486        view.end_selection(cx);
  487    });
  488
  489    assert_eq!(
  490        editor
  491            .update(cx, |view, cx| view.selections.display_ranges(cx))
  492            .unwrap(),
  493        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  494    );
  495}
  496
  497#[gpui::test]
  498fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  499    init_test(cx, |_| {});
  500
  501    let view = cx.add_window(|cx| {
  502        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  503        build_editor(buffer, cx)
  504    });
  505
  506    _ = view.update(cx, |view, cx| {
  507        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  508        assert_eq!(
  509            view.selections.display_ranges(cx),
  510            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  511        );
  512    });
  513
  514    _ = view.update(cx, |view, cx| {
  515        view.update_selection(
  516            DisplayPoint::new(DisplayRow(3), 3),
  517            0,
  518            gpui::Point::<f32>::default(),
  519            cx,
  520        );
  521        assert_eq!(
  522            view.selections.display_ranges(cx),
  523            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  524        );
  525    });
  526
  527    _ = view.update(cx, |view, cx| {
  528        view.cancel(&Cancel, cx);
  529        view.update_selection(
  530            DisplayPoint::new(DisplayRow(1), 1),
  531            0,
  532            gpui::Point::<f32>::default(),
  533            cx,
  534        );
  535        assert_eq!(
  536            view.selections.display_ranges(cx),
  537            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  538        );
  539    });
  540}
  541
  542#[gpui::test]
  543fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  544    init_test(cx, |_| {});
  545
  546    let view = cx.add_window(|cx| {
  547        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  548        build_editor(buffer, cx)
  549    });
  550
  551    _ = view.update(cx, |view, cx| {
  552        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  553        assert_eq!(
  554            view.selections.display_ranges(cx),
  555            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  556        );
  557
  558        view.move_down(&Default::default(), cx);
  559        assert_eq!(
  560            view.selections.display_ranges(cx),
  561            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  562        );
  563
  564        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  565        assert_eq!(
  566            view.selections.display_ranges(cx),
  567            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  568        );
  569
  570        view.move_up(&Default::default(), cx);
  571        assert_eq!(
  572            view.selections.display_ranges(cx),
  573            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  574        );
  575    });
  576}
  577
  578#[gpui::test]
  579fn test_clone(cx: &mut TestAppContext) {
  580    init_test(cx, |_| {});
  581
  582    let (text, selection_ranges) = marked_text_ranges(
  583        indoc! {"
  584            one
  585            two
  586            threeˇ
  587            four
  588            fiveˇ
  589        "},
  590        true,
  591    );
  592
  593    let editor = cx.add_window(|cx| {
  594        let buffer = MultiBuffer::build_simple(&text, cx);
  595        build_editor(buffer, cx)
  596    });
  597
  598    _ = editor.update(cx, |editor, cx| {
  599        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  600        editor.fold_creases(
  601            vec![
  602                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  603                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  604            ],
  605            true,
  606            cx,
  607        );
  608    });
  609
  610    let cloned_editor = editor
  611        .update(cx, |editor, cx| {
  612            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  613        })
  614        .unwrap()
  615        .unwrap();
  616
  617    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  618    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  619
  620    assert_eq!(
  621        cloned_editor
  622            .update(cx, |e, cx| e.display_text(cx))
  623            .unwrap(),
  624        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  625    );
  626    assert_eq!(
  627        cloned_snapshot
  628            .folds_in_range(0..text.len())
  629            .collect::<Vec<_>>(),
  630        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  631    );
  632    assert_set_eq!(
  633        cloned_editor
  634            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  635            .unwrap(),
  636        editor
  637            .update(cx, |editor, cx| editor.selections.ranges(cx))
  638            .unwrap()
  639    );
  640    assert_set_eq!(
  641        cloned_editor
  642            .update(cx, |e, cx| e.selections.display_ranges(cx))
  643            .unwrap(),
  644        editor
  645            .update(cx, |e, cx| e.selections.display_ranges(cx))
  646            .unwrap()
  647    );
  648}
  649
  650#[gpui::test]
  651async fn test_navigation_history(cx: &mut TestAppContext) {
  652    init_test(cx, |_| {});
  653
  654    use workspace::item::Item;
  655
  656    let fs = FakeFs::new(cx.executor());
  657    let project = Project::test(fs, [], cx).await;
  658    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  659    let pane = workspace
  660        .update(cx, |workspace, _| workspace.active_pane().clone())
  661        .unwrap();
  662
  663    _ = workspace.update(cx, |_v, cx| {
  664        cx.new_view(|cx| {
  665            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  666            let mut editor = build_editor(buffer.clone(), cx);
  667            let handle = cx.view();
  668            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  669
  670            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  671                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  672            }
  673
  674            // Move the cursor a small distance.
  675            // Nothing is added to the navigation history.
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  679                ])
  680            });
  681            editor.change_selections(None, cx, |s| {
  682                s.select_display_ranges([
  683                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  684                ])
  685            });
  686            assert!(pop_history(&mut editor, cx).is_none());
  687
  688            // Move the cursor a large distance.
  689            // The history can jump back to the previous position.
  690            editor.change_selections(None, cx, |s| {
  691                s.select_display_ranges([
  692                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  693                ])
  694            });
  695            let nav_entry = pop_history(&mut editor, cx).unwrap();
  696            editor.navigate(nav_entry.data.unwrap(), cx);
  697            assert_eq!(nav_entry.item.id(), cx.entity_id());
  698            assert_eq!(
  699                editor.selections.display_ranges(cx),
  700                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  701            );
  702            assert!(pop_history(&mut editor, cx).is_none());
  703
  704            // Move the cursor a small distance via the mouse.
  705            // Nothing is added to the navigation history.
  706            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  707            editor.end_selection(cx);
  708            assert_eq!(
  709                editor.selections.display_ranges(cx),
  710                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  711            );
  712            assert!(pop_history(&mut editor, cx).is_none());
  713
  714            // Move the cursor a large distance via the mouse.
  715            // The history can jump back to the previous position.
  716            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  717            editor.end_selection(cx);
  718            assert_eq!(
  719                editor.selections.display_ranges(cx),
  720                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  721            );
  722            let nav_entry = pop_history(&mut editor, cx).unwrap();
  723            editor.navigate(nav_entry.data.unwrap(), cx);
  724            assert_eq!(nav_entry.item.id(), cx.entity_id());
  725            assert_eq!(
  726                editor.selections.display_ranges(cx),
  727                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  728            );
  729            assert!(pop_history(&mut editor, cx).is_none());
  730
  731            // Set scroll position to check later
  732            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  733            let original_scroll_position = editor.scroll_manager.anchor();
  734
  735            // Jump to the end of the document and adjust scroll
  736            editor.move_to_end(&MoveToEnd, cx);
  737            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  738            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            let nav_entry = pop_history(&mut editor, cx).unwrap();
  741            editor.navigate(nav_entry.data.unwrap(), cx);
  742            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  743
  744            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  745            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  746            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  747            let invalid_point = Point::new(9999, 0);
  748            editor.navigate(
  749                Box::new(NavigationData {
  750                    cursor_anchor: invalid_anchor,
  751                    cursor_position: invalid_point,
  752                    scroll_anchor: ScrollAnchor {
  753                        anchor: invalid_anchor,
  754                        offset: Default::default(),
  755                    },
  756                    scroll_top_row: invalid_point.row,
  757                }),
  758                cx,
  759            );
  760            assert_eq!(
  761                editor.selections.display_ranges(cx),
  762                &[editor.max_point(cx)..editor.max_point(cx)]
  763            );
  764            assert_eq!(
  765                editor.scroll_position(cx),
  766                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  767            );
  768
  769            editor
  770        })
  771    });
  772}
  773
  774#[gpui::test]
  775fn test_cancel(cx: &mut TestAppContext) {
  776    init_test(cx, |_| {});
  777
  778    let view = cx.add_window(|cx| {
  779        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  780        build_editor(buffer, cx)
  781    });
  782
  783    _ = view.update(cx, |view, cx| {
  784        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  785        view.update_selection(
  786            DisplayPoint::new(DisplayRow(1), 1),
  787            0,
  788            gpui::Point::<f32>::default(),
  789            cx,
  790        );
  791        view.end_selection(cx);
  792
  793        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  794        view.update_selection(
  795            DisplayPoint::new(DisplayRow(0), 3),
  796            0,
  797            gpui::Point::<f32>::default(),
  798            cx,
  799        );
  800        view.end_selection(cx);
  801        assert_eq!(
  802            view.selections.display_ranges(cx),
  803            [
  804                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  805                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  806            ]
  807        );
  808    });
  809
  810    _ = view.update(cx, |view, cx| {
  811        view.cancel(&Cancel, cx);
  812        assert_eq!(
  813            view.selections.display_ranges(cx),
  814            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  815        );
  816    });
  817
  818    _ = view.update(cx, |view, cx| {
  819        view.cancel(&Cancel, cx);
  820        assert_eq!(
  821            view.selections.display_ranges(cx),
  822            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  823        );
  824    });
  825}
  826
  827#[gpui::test]
  828fn test_fold_action(cx: &mut TestAppContext) {
  829    init_test(cx, |_| {});
  830
  831    let view = cx.add_window(|cx| {
  832        let buffer = MultiBuffer::build_simple(
  833            &"
  834                impl Foo {
  835                    // Hello!
  836
  837                    fn a() {
  838                        1
  839                    }
  840
  841                    fn b() {
  842                        2
  843                    }
  844
  845                    fn c() {
  846                        3
  847                    }
  848                }
  849            "
  850            .unindent(),
  851            cx,
  852        );
  853        build_editor(buffer.clone(), cx)
  854    });
  855
  856    _ = view.update(cx, |view, cx| {
  857        view.change_selections(None, cx, |s| {
  858            s.select_display_ranges([
  859                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  860            ]);
  861        });
  862        view.fold(&Fold, cx);
  863        assert_eq!(
  864            view.display_text(cx),
  865            "
  866                impl Foo {
  867                    // Hello!
  868
  869                    fn a() {
  870                        1
  871                    }
  872
  873                    fn b() {⋯
  874                    }
  875
  876                    fn c() {⋯
  877                    }
  878                }
  879            "
  880            .unindent(),
  881        );
  882
  883        view.fold(&Fold, cx);
  884        assert_eq!(
  885            view.display_text(cx),
  886            "
  887                impl Foo {⋯
  888                }
  889            "
  890            .unindent(),
  891        );
  892
  893        view.unfold_lines(&UnfoldLines, cx);
  894        assert_eq!(
  895            view.display_text(cx),
  896            "
  897                impl Foo {
  898                    // Hello!
  899
  900                    fn a() {
  901                        1
  902                    }
  903
  904                    fn b() {⋯
  905                    }
  906
  907                    fn c() {⋯
  908                    }
  909                }
  910            "
  911            .unindent(),
  912        );
  913
  914        view.unfold_lines(&UnfoldLines, cx);
  915        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  916    });
  917}
  918
  919#[gpui::test]
  920fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  921    init_test(cx, |_| {});
  922
  923    let view = cx.add_window(|cx| {
  924        let buffer = MultiBuffer::build_simple(
  925            &"
  926                class Foo:
  927                    # Hello!
  928
  929                    def a():
  930                        print(1)
  931
  932                    def b():
  933                        print(2)
  934
  935                    def c():
  936                        print(3)
  937            "
  938            .unindent(),
  939            cx,
  940        );
  941        build_editor(buffer.clone(), cx)
  942    });
  943
  944    _ = view.update(cx, |view, cx| {
  945        view.change_selections(None, cx, |s| {
  946            s.select_display_ranges([
  947                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  948            ]);
  949        });
  950        view.fold(&Fold, cx);
  951        assert_eq!(
  952            view.display_text(cx),
  953            "
  954                class Foo:
  955                    # Hello!
  956
  957                    def a():
  958                        print(1)
  959
  960                    def b():⋯
  961
  962                    def c():⋯
  963            "
  964            .unindent(),
  965        );
  966
  967        view.fold(&Fold, cx);
  968        assert_eq!(
  969            view.display_text(cx),
  970            "
  971                class Foo:⋯
  972            "
  973            .unindent(),
  974        );
  975
  976        view.unfold_lines(&UnfoldLines, cx);
  977        assert_eq!(
  978            view.display_text(cx),
  979            "
  980                class Foo:
  981                    # Hello!
  982
  983                    def a():
  984                        print(1)
  985
  986                    def b():⋯
  987
  988                    def c():⋯
  989            "
  990            .unindent(),
  991        );
  992
  993        view.unfold_lines(&UnfoldLines, cx);
  994        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  995    });
  996}
  997
  998#[gpui::test]
  999fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1000    init_test(cx, |_| {});
 1001
 1002    let view = cx.add_window(|cx| {
 1003        let buffer = MultiBuffer::build_simple(
 1004            &"
 1005                class Foo:
 1006                    # Hello!
 1007
 1008                    def a():
 1009                        print(1)
 1010
 1011                    def b():
 1012                        print(2)
 1013
 1014
 1015                    def c():
 1016                        print(3)
 1017
 1018
 1019            "
 1020            .unindent(),
 1021            cx,
 1022        );
 1023        build_editor(buffer.clone(), cx)
 1024    });
 1025
 1026    _ = view.update(cx, |view, cx| {
 1027        view.change_selections(None, cx, |s| {
 1028            s.select_display_ranges([
 1029                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1030            ]);
 1031        });
 1032        view.fold(&Fold, cx);
 1033        assert_eq!(
 1034            view.display_text(cx),
 1035            "
 1036                class Foo:
 1037                    # Hello!
 1038
 1039                    def a():
 1040                        print(1)
 1041
 1042                    def b():⋯
 1043
 1044
 1045                    def c():⋯
 1046
 1047
 1048            "
 1049            .unindent(),
 1050        );
 1051
 1052        view.fold(&Fold, cx);
 1053        assert_eq!(
 1054            view.display_text(cx),
 1055            "
 1056                class Foo:⋯
 1057
 1058
 1059            "
 1060            .unindent(),
 1061        );
 1062
 1063        view.unfold_lines(&UnfoldLines, cx);
 1064        assert_eq!(
 1065            view.display_text(cx),
 1066            "
 1067                class Foo:
 1068                    # Hello!
 1069
 1070                    def a():
 1071                        print(1)
 1072
 1073                    def b():⋯
 1074
 1075
 1076                    def c():⋯
 1077
 1078
 1079            "
 1080            .unindent(),
 1081        );
 1082
 1083        view.unfold_lines(&UnfoldLines, cx);
 1084        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1085    });
 1086}
 1087
 1088#[gpui::test]
 1089fn test_fold_at_level(cx: &mut TestAppContext) {
 1090    init_test(cx, |_| {});
 1091
 1092    let view = cx.add_window(|cx| {
 1093        let buffer = MultiBuffer::build_simple(
 1094            &"
 1095                class Foo:
 1096                    # Hello!
 1097
 1098                    def a():
 1099                        print(1)
 1100
 1101                    def b():
 1102                        print(2)
 1103
 1104
 1105                class Bar:
 1106                    # World!
 1107
 1108                    def a():
 1109                        print(1)
 1110
 1111                    def b():
 1112                        print(2)
 1113
 1114
 1115            "
 1116            .unindent(),
 1117            cx,
 1118        );
 1119        build_editor(buffer.clone(), cx)
 1120    });
 1121
 1122    _ = view.update(cx, |view, cx| {
 1123        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1124        assert_eq!(
 1125            view.display_text(cx),
 1126            "
 1127                class Foo:
 1128                    # Hello!
 1129
 1130                    def a():⋯
 1131
 1132                    def b():⋯
 1133
 1134
 1135                class Bar:
 1136                    # World!
 1137
 1138                    def a():⋯
 1139
 1140                    def b():⋯
 1141
 1142
 1143            "
 1144            .unindent(),
 1145        );
 1146
 1147        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1148        assert_eq!(
 1149            view.display_text(cx),
 1150            "
 1151                class Foo:⋯
 1152
 1153
 1154                class Bar:⋯
 1155
 1156
 1157            "
 1158            .unindent(),
 1159        );
 1160
 1161        view.unfold_all(&UnfoldAll, cx);
 1162        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1163        assert_eq!(
 1164            view.display_text(cx),
 1165            "
 1166                class Foo:
 1167                    # Hello!
 1168
 1169                    def a():
 1170                        print(1)
 1171
 1172                    def b():
 1173                        print(2)
 1174
 1175
 1176                class Bar:
 1177                    # World!
 1178
 1179                    def a():
 1180                        print(1)
 1181
 1182                    def b():
 1183                        print(2)
 1184
 1185
 1186            "
 1187            .unindent(),
 1188        );
 1189
 1190        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1191    });
 1192}
 1193
 1194#[gpui::test]
 1195fn test_move_cursor(cx: &mut TestAppContext) {
 1196    init_test(cx, |_| {});
 1197
 1198    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1199    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1200
 1201    buffer.update(cx, |buffer, cx| {
 1202        buffer.edit(
 1203            vec![
 1204                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1205                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1206            ],
 1207            None,
 1208            cx,
 1209        );
 1210    });
 1211    _ = view.update(cx, |view, cx| {
 1212        assert_eq!(
 1213            view.selections.display_ranges(cx),
 1214            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1215        );
 1216
 1217        view.move_down(&MoveDown, cx);
 1218        assert_eq!(
 1219            view.selections.display_ranges(cx),
 1220            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1221        );
 1222
 1223        view.move_right(&MoveRight, cx);
 1224        assert_eq!(
 1225            view.selections.display_ranges(cx),
 1226            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1227        );
 1228
 1229        view.move_left(&MoveLeft, cx);
 1230        assert_eq!(
 1231            view.selections.display_ranges(cx),
 1232            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1233        );
 1234
 1235        view.move_up(&MoveUp, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1239        );
 1240
 1241        view.move_to_end(&MoveToEnd, cx);
 1242        assert_eq!(
 1243            view.selections.display_ranges(cx),
 1244            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1245        );
 1246
 1247        view.move_to_beginning(&MoveToBeginning, cx);
 1248        assert_eq!(
 1249            view.selections.display_ranges(cx),
 1250            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1251        );
 1252
 1253        view.change_selections(None, cx, |s| {
 1254            s.select_display_ranges([
 1255                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1256            ]);
 1257        });
 1258        view.select_to_beginning(&SelectToBeginning, cx);
 1259        assert_eq!(
 1260            view.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        view.select_to_end(&SelectToEnd, cx);
 1265        assert_eq!(
 1266            view.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1268        );
 1269    });
 1270}
 1271
 1272// TODO: Re-enable this test
 1273#[cfg(target_os = "macos")]
 1274#[gpui::test]
 1275fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1276    init_test(cx, |_| {});
 1277
 1278    let view = cx.add_window(|cx| {
 1279        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1280        build_editor(buffer.clone(), cx)
 1281    });
 1282
 1283    assert_eq!('ⓐ'.len_utf8(), 3);
 1284    assert_eq!('α'.len_utf8(), 2);
 1285
 1286    _ = view.update(cx, |view, cx| {
 1287        view.fold_creases(
 1288            vec![
 1289                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1290                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1291                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1292            ],
 1293            true,
 1294            cx,
 1295        );
 1296        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1297
 1298        view.move_right(&MoveRight, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(0, "".len())]
 1302        );
 1303        view.move_right(&MoveRight, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(0, "ⓐⓑ".len())]
 1307        );
 1308        view.move_right(&MoveRight, cx);
 1309        assert_eq!(
 1310            view.selections.display_ranges(cx),
 1311            &[empty_range(0, "ⓐⓑ⋯".len())]
 1312        );
 1313
 1314        view.move_down(&MoveDown, cx);
 1315        assert_eq!(
 1316            view.selections.display_ranges(cx),
 1317            &[empty_range(1, "ab⋯e".len())]
 1318        );
 1319        view.move_left(&MoveLeft, cx);
 1320        assert_eq!(
 1321            view.selections.display_ranges(cx),
 1322            &[empty_range(1, "ab⋯".len())]
 1323        );
 1324        view.move_left(&MoveLeft, cx);
 1325        assert_eq!(
 1326            view.selections.display_ranges(cx),
 1327            &[empty_range(1, "ab".len())]
 1328        );
 1329        view.move_left(&MoveLeft, cx);
 1330        assert_eq!(
 1331            view.selections.display_ranges(cx),
 1332            &[empty_range(1, "a".len())]
 1333        );
 1334
 1335        view.move_down(&MoveDown, cx);
 1336        assert_eq!(
 1337            view.selections.display_ranges(cx),
 1338            &[empty_range(2, "α".len())]
 1339        );
 1340        view.move_right(&MoveRight, cx);
 1341        assert_eq!(
 1342            view.selections.display_ranges(cx),
 1343            &[empty_range(2, "αβ".len())]
 1344        );
 1345        view.move_right(&MoveRight, cx);
 1346        assert_eq!(
 1347            view.selections.display_ranges(cx),
 1348            &[empty_range(2, "αβ⋯".len())]
 1349        );
 1350        view.move_right(&MoveRight, cx);
 1351        assert_eq!(
 1352            view.selections.display_ranges(cx),
 1353            &[empty_range(2, "αβ⋯ε".len())]
 1354        );
 1355
 1356        view.move_up(&MoveUp, cx);
 1357        assert_eq!(
 1358            view.selections.display_ranges(cx),
 1359            &[empty_range(1, "ab⋯e".len())]
 1360        );
 1361        view.move_down(&MoveDown, cx);
 1362        assert_eq!(
 1363            view.selections.display_ranges(cx),
 1364            &[empty_range(2, "αβ⋯ε".len())]
 1365        );
 1366        view.move_up(&MoveUp, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯e".len())]
 1370        );
 1371
 1372        view.move_up(&MoveUp, cx);
 1373        assert_eq!(
 1374            view.selections.display_ranges(cx),
 1375            &[empty_range(0, "ⓐⓑ".len())]
 1376        );
 1377        view.move_left(&MoveLeft, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[empty_range(0, "".len())]
 1381        );
 1382        view.move_left(&MoveLeft, cx);
 1383        assert_eq!(
 1384            view.selections.display_ranges(cx),
 1385            &[empty_range(0, "".len())]
 1386        );
 1387    });
 1388}
 1389
 1390#[gpui::test]
 1391fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1392    init_test(cx, |_| {});
 1393
 1394    let view = cx.add_window(|cx| {
 1395        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1396        build_editor(buffer.clone(), cx)
 1397    });
 1398    _ = view.update(cx, |view, cx| {
 1399        view.change_selections(None, cx, |s| {
 1400            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1401        });
 1402
 1403        // moving above start of document should move selection to start of document,
 1404        // but the next move down should still be at the original goal_x
 1405        view.move_up(&MoveUp, cx);
 1406        assert_eq!(
 1407            view.selections.display_ranges(cx),
 1408            &[empty_range(0, "".len())]
 1409        );
 1410
 1411        view.move_down(&MoveDown, cx);
 1412        assert_eq!(
 1413            view.selections.display_ranges(cx),
 1414            &[empty_range(1, "abcd".len())]
 1415        );
 1416
 1417        view.move_down(&MoveDown, cx);
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[empty_range(2, "αβγ".len())]
 1421        );
 1422
 1423        view.move_down(&MoveDown, cx);
 1424        assert_eq!(
 1425            view.selections.display_ranges(cx),
 1426            &[empty_range(3, "abcd".len())]
 1427        );
 1428
 1429        view.move_down(&MoveDown, cx);
 1430        assert_eq!(
 1431            view.selections.display_ranges(cx),
 1432            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1433        );
 1434
 1435        // moving past end of document should not change goal_x
 1436        view.move_down(&MoveDown, cx);
 1437        assert_eq!(
 1438            view.selections.display_ranges(cx),
 1439            &[empty_range(5, "".len())]
 1440        );
 1441
 1442        view.move_down(&MoveDown, cx);
 1443        assert_eq!(
 1444            view.selections.display_ranges(cx),
 1445            &[empty_range(5, "".len())]
 1446        );
 1447
 1448        view.move_up(&MoveUp, cx);
 1449        assert_eq!(
 1450            view.selections.display_ranges(cx),
 1451            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1452        );
 1453
 1454        view.move_up(&MoveUp, cx);
 1455        assert_eq!(
 1456            view.selections.display_ranges(cx),
 1457            &[empty_range(3, "abcd".len())]
 1458        );
 1459
 1460        view.move_up(&MoveUp, cx);
 1461        assert_eq!(
 1462            view.selections.display_ranges(cx),
 1463            &[empty_range(2, "αβγ".len())]
 1464        );
 1465    });
 1466}
 1467
 1468#[gpui::test]
 1469fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1470    init_test(cx, |_| {});
 1471    let move_to_beg = MoveToBeginningOfLine {
 1472        stop_at_soft_wraps: true,
 1473    };
 1474
 1475    let move_to_end = MoveToEndOfLine {
 1476        stop_at_soft_wraps: true,
 1477    };
 1478
 1479    let view = cx.add_window(|cx| {
 1480        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1481        build_editor(buffer, cx)
 1482    });
 1483    _ = view.update(cx, |view, cx| {
 1484        view.change_selections(None, cx, |s| {
 1485            s.select_display_ranges([
 1486                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1487                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1488            ]);
 1489        });
 1490    });
 1491
 1492    _ = view.update(cx, |view, cx| {
 1493        view.move_to_beginning_of_line(&move_to_beg, cx);
 1494        assert_eq!(
 1495            view.selections.display_ranges(cx),
 1496            &[
 1497                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1498                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1499            ]
 1500        );
 1501    });
 1502
 1503    _ = view.update(cx, |view, cx| {
 1504        view.move_to_beginning_of_line(&move_to_beg, cx);
 1505        assert_eq!(
 1506            view.selections.display_ranges(cx),
 1507            &[
 1508                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1509                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1510            ]
 1511        );
 1512    });
 1513
 1514    _ = view.update(cx, |view, cx| {
 1515        view.move_to_beginning_of_line(&move_to_beg, cx);
 1516        assert_eq!(
 1517            view.selections.display_ranges(cx),
 1518            &[
 1519                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1520                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1521            ]
 1522        );
 1523    });
 1524
 1525    _ = view.update(cx, |view, cx| {
 1526        view.move_to_end_of_line(&move_to_end, cx);
 1527        assert_eq!(
 1528            view.selections.display_ranges(cx),
 1529            &[
 1530                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1531                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1532            ]
 1533        );
 1534    });
 1535
 1536    // Moving to the end of line again is a no-op.
 1537    _ = view.update(cx, |view, cx| {
 1538        view.move_to_end_of_line(&move_to_end, cx);
 1539        assert_eq!(
 1540            view.selections.display_ranges(cx),
 1541            &[
 1542                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1543                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1544            ]
 1545        );
 1546    });
 1547
 1548    _ = view.update(cx, |view, cx| {
 1549        view.move_left(&MoveLeft, cx);
 1550        view.select_to_beginning_of_line(
 1551            &SelectToBeginningOfLine {
 1552                stop_at_soft_wraps: true,
 1553            },
 1554            cx,
 1555        );
 1556        assert_eq!(
 1557            view.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = view.update(cx, |view, cx| {
 1566        view.select_to_beginning_of_line(
 1567            &SelectToBeginningOfLine {
 1568                stop_at_soft_wraps: true,
 1569            },
 1570            cx,
 1571        );
 1572        assert_eq!(
 1573            view.selections.display_ranges(cx),
 1574            &[
 1575                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1576                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1577            ]
 1578        );
 1579    });
 1580
 1581    _ = view.update(cx, |view, cx| {
 1582        view.select_to_beginning_of_line(
 1583            &SelectToBeginningOfLine {
 1584                stop_at_soft_wraps: true,
 1585            },
 1586            cx,
 1587        );
 1588        assert_eq!(
 1589            view.selections.display_ranges(cx),
 1590            &[
 1591                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1592                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1593            ]
 1594        );
 1595    });
 1596
 1597    _ = view.update(cx, |view, cx| {
 1598        view.select_to_end_of_line(
 1599            &SelectToEndOfLine {
 1600                stop_at_soft_wraps: true,
 1601            },
 1602            cx,
 1603        );
 1604        assert_eq!(
 1605            view.selections.display_ranges(cx),
 1606            &[
 1607                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1608                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1609            ]
 1610        );
 1611    });
 1612
 1613    _ = view.update(cx, |view, cx| {
 1614        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1615        assert_eq!(view.display_text(cx), "ab\n  de");
 1616        assert_eq!(
 1617            view.selections.display_ranges(cx),
 1618            &[
 1619                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1620                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1621            ]
 1622        );
 1623    });
 1624
 1625    _ = view.update(cx, |view, cx| {
 1626        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1627        assert_eq!(view.display_text(cx), "\n");
 1628        assert_eq!(
 1629            view.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636}
 1637
 1638#[gpui::test]
 1639fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1640    init_test(cx, |_| {});
 1641    let move_to_beg = MoveToBeginningOfLine {
 1642        stop_at_soft_wraps: false,
 1643    };
 1644
 1645    let move_to_end = MoveToEndOfLine {
 1646        stop_at_soft_wraps: false,
 1647    };
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", 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
 1657        // We expect the following lines after wrapping
 1658        // ```
 1659        // thequickbrownfox
 1660        // jumpedoverthelazydo
 1661        // gs
 1662        // ```
 1663        // The final `gs` was soft-wrapped onto a new line.
 1664        assert_eq!(
 1665            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1666            view.display_text(cx),
 1667        );
 1668
 1669        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1670        // Start the cursor at the `k` on the first line
 1671        view.change_selections(None, cx, |s| {
 1672            s.select_display_ranges([
 1673                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1674            ]);
 1675        });
 1676
 1677        // Moving to the beginning of the line should put us at the beginning of the line.
 1678        view.move_to_beginning_of_line(&move_to_beg, cx);
 1679        assert_eq!(
 1680            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1681            view.selections.display_ranges(cx)
 1682        );
 1683
 1684        // Moving to the end of the line should put us at the end of the line.
 1685        view.move_to_end_of_line(&move_to_end, cx);
 1686        assert_eq!(
 1687            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1688            view.selections.display_ranges(cx)
 1689        );
 1690
 1691        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1692        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1693        view.change_selections(None, cx, |s| {
 1694            s.select_display_ranges([
 1695                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1696            ]);
 1697        });
 1698
 1699        // Moving to the beginning of the line should put us at the start of the second line of
 1700        // display text, i.e., the `j`.
 1701        view.move_to_beginning_of_line(&move_to_beg, cx);
 1702        assert_eq!(
 1703            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1704            view.selections.display_ranges(cx)
 1705        );
 1706
 1707        // Moving to the beginning of the line again should be a no-op.
 1708        view.move_to_beginning_of_line(&move_to_beg, cx);
 1709        assert_eq!(
 1710            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1711            view.selections.display_ranges(cx)
 1712        );
 1713
 1714        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1715        // next display line.
 1716        view.move_to_end_of_line(&move_to_end, cx);
 1717        assert_eq!(
 1718            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1719            view.selections.display_ranges(cx)
 1720        );
 1721
 1722        // Moving to the end of the line again should be a no-op.
 1723        view.move_to_end_of_line(&move_to_end, cx);
 1724        assert_eq!(
 1725            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1726            view.selections.display_ranges(cx)
 1727        );
 1728    });
 1729}
 1730
 1731#[gpui::test]
 1732fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1733    init_test(cx, |_| {});
 1734
 1735    let view = cx.add_window(|cx| {
 1736        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1737        build_editor(buffer, cx)
 1738    });
 1739    _ = view.update(cx, |view, cx| {
 1740        view.change_selections(None, cx, |s| {
 1741            s.select_display_ranges([
 1742                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1743                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1744            ])
 1745        });
 1746
 1747        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1748        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1749
 1750        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1751        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1752
 1753        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1754        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1755
 1756        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1757        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1758
 1759        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1760        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1761
 1762        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1763        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1764
 1765        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1766        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1767
 1768        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1769        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1770
 1771        view.move_right(&MoveRight, cx);
 1772        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1773        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1774
 1775        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1776        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1777
 1778        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1779        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1780    });
 1781}
 1782
 1783#[gpui::test]
 1784fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1785    init_test(cx, |_| {});
 1786
 1787    let view = cx.add_window(|cx| {
 1788        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1789        build_editor(buffer, cx)
 1790    });
 1791
 1792    _ = view.update(cx, |view, cx| {
 1793        view.set_wrap_width(Some(140.0.into()), cx);
 1794        assert_eq!(
 1795            view.display_text(cx),
 1796            "use one::{\n    two::three::\n    four::five\n};"
 1797        );
 1798
 1799        view.change_selections(None, cx, |s| {
 1800            s.select_display_ranges([
 1801                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1802            ]);
 1803        });
 1804
 1805        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1806        assert_eq!(
 1807            view.selections.display_ranges(cx),
 1808            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1809        );
 1810
 1811        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1812        assert_eq!(
 1813            view.selections.display_ranges(cx),
 1814            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1815        );
 1816
 1817        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1818        assert_eq!(
 1819            view.selections.display_ranges(cx),
 1820            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1821        );
 1822
 1823        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1824        assert_eq!(
 1825            view.selections.display_ranges(cx),
 1826            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1827        );
 1828
 1829        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1830        assert_eq!(
 1831            view.selections.display_ranges(cx),
 1832            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1833        );
 1834
 1835        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1836        assert_eq!(
 1837            view.selections.display_ranges(cx),
 1838            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1839        );
 1840    });
 1841}
 1842
 1843#[gpui::test]
 1844async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1845    init_test(cx, |_| {});
 1846    let mut cx = EditorTestContext::new(cx).await;
 1847
 1848    let line_height = cx.editor(|editor, cx| {
 1849        editor
 1850            .style()
 1851            .unwrap()
 1852            .text
 1853            .line_height_in_pixels(cx.rem_size())
 1854    });
 1855    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1856
 1857    cx.set_state(
 1858        &r#"ˇone
 1859        two
 1860
 1861        three
 1862        fourˇ
 1863        five
 1864
 1865        six"#
 1866            .unindent(),
 1867    );
 1868
 1869    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1870    cx.assert_editor_state(
 1871        &r#"one
 1872        two
 1873        ˇ
 1874        three
 1875        four
 1876        five
 1877        ˇ
 1878        six"#
 1879            .unindent(),
 1880    );
 1881
 1882    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1883    cx.assert_editor_state(
 1884        &r#"one
 1885        two
 1886
 1887        three
 1888        four
 1889        five
 1890        ˇ
 1891        sixˇ"#
 1892            .unindent(),
 1893    );
 1894
 1895    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1896    cx.assert_editor_state(
 1897        &r#"one
 1898        two
 1899
 1900        three
 1901        four
 1902        five
 1903
 1904        sixˇ"#
 1905            .unindent(),
 1906    );
 1907
 1908    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1909    cx.assert_editor_state(
 1910        &r#"one
 1911        two
 1912
 1913        three
 1914        four
 1915        five
 1916        ˇ
 1917        six"#
 1918            .unindent(),
 1919    );
 1920
 1921    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1922    cx.assert_editor_state(
 1923        &r#"one
 1924        two
 1925        ˇ
 1926        three
 1927        four
 1928        five
 1929
 1930        six"#
 1931            .unindent(),
 1932    );
 1933
 1934    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1935    cx.assert_editor_state(
 1936        &r#"ˇone
 1937        two
 1938
 1939        three
 1940        four
 1941        five
 1942
 1943        six"#
 1944            .unindent(),
 1945    );
 1946}
 1947
 1948#[gpui::test]
 1949async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1950    init_test(cx, |_| {});
 1951    let mut cx = EditorTestContext::new(cx).await;
 1952    let line_height = cx.editor(|editor, cx| {
 1953        editor
 1954            .style()
 1955            .unwrap()
 1956            .text
 1957            .line_height_in_pixels(cx.rem_size())
 1958    });
 1959    let window = cx.window;
 1960    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1961
 1962    cx.set_state(
 1963        r#"ˇone
 1964        two
 1965        three
 1966        four
 1967        five
 1968        six
 1969        seven
 1970        eight
 1971        nine
 1972        ten
 1973        "#,
 1974    );
 1975
 1976    cx.update_editor(|editor, cx| {
 1977        assert_eq!(
 1978            editor.snapshot(cx).scroll_position(),
 1979            gpui::Point::new(0., 0.)
 1980        );
 1981        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1982        assert_eq!(
 1983            editor.snapshot(cx).scroll_position(),
 1984            gpui::Point::new(0., 3.)
 1985        );
 1986        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1987        assert_eq!(
 1988            editor.snapshot(cx).scroll_position(),
 1989            gpui::Point::new(0., 6.)
 1990        );
 1991        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1992        assert_eq!(
 1993            editor.snapshot(cx).scroll_position(),
 1994            gpui::Point::new(0., 3.)
 1995        );
 1996
 1997        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1998        assert_eq!(
 1999            editor.snapshot(cx).scroll_position(),
 2000            gpui::Point::new(0., 1.)
 2001        );
 2002        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2003        assert_eq!(
 2004            editor.snapshot(cx).scroll_position(),
 2005            gpui::Point::new(0., 3.)
 2006        );
 2007    });
 2008}
 2009
 2010#[gpui::test]
 2011async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2012    init_test(cx, |_| {});
 2013    let mut cx = EditorTestContext::new(cx).await;
 2014
 2015    let line_height = cx.update_editor(|editor, cx| {
 2016        editor.set_vertical_scroll_margin(2, cx);
 2017        editor
 2018            .style()
 2019            .unwrap()
 2020            .text
 2021            .line_height_in_pixels(cx.rem_size())
 2022    });
 2023    let window = cx.window;
 2024    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2025
 2026    cx.set_state(
 2027        r#"ˇone
 2028            two
 2029            three
 2030            four
 2031            five
 2032            six
 2033            seven
 2034            eight
 2035            nine
 2036            ten
 2037        "#,
 2038    );
 2039    cx.update_editor(|editor, cx| {
 2040        assert_eq!(
 2041            editor.snapshot(cx).scroll_position(),
 2042            gpui::Point::new(0., 0.0)
 2043        );
 2044    });
 2045
 2046    // Add a cursor below the visible area. Since both cursors cannot fit
 2047    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2048    // allows the vertical scroll margin below that cursor.
 2049    cx.update_editor(|editor, cx| {
 2050        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2051            selections.select_ranges([
 2052                Point::new(0, 0)..Point::new(0, 0),
 2053                Point::new(6, 0)..Point::new(6, 0),
 2054            ]);
 2055        })
 2056    });
 2057    cx.update_editor(|editor, cx| {
 2058        assert_eq!(
 2059            editor.snapshot(cx).scroll_position(),
 2060            gpui::Point::new(0., 3.0)
 2061        );
 2062    });
 2063
 2064    // Move down. The editor cursor scrolls down to track the newest cursor.
 2065    cx.update_editor(|editor, cx| {
 2066        editor.move_down(&Default::default(), cx);
 2067    });
 2068    cx.update_editor(|editor, cx| {
 2069        assert_eq!(
 2070            editor.snapshot(cx).scroll_position(),
 2071            gpui::Point::new(0., 4.0)
 2072        );
 2073    });
 2074
 2075    // Add a cursor above the visible area. Since both cursors fit on screen,
 2076    // the editor scrolls to show both.
 2077    cx.update_editor(|editor, cx| {
 2078        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2079            selections.select_ranges([
 2080                Point::new(1, 0)..Point::new(1, 0),
 2081                Point::new(6, 0)..Point::new(6, 0),
 2082            ]);
 2083        })
 2084    });
 2085    cx.update_editor(|editor, cx| {
 2086        assert_eq!(
 2087            editor.snapshot(cx).scroll_position(),
 2088            gpui::Point::new(0., 1.0)
 2089        );
 2090    });
 2091}
 2092
 2093#[gpui::test]
 2094async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2095    init_test(cx, |_| {});
 2096    let mut cx = EditorTestContext::new(cx).await;
 2097
 2098    let line_height = cx.editor(|editor, cx| {
 2099        editor
 2100            .style()
 2101            .unwrap()
 2102            .text
 2103            .line_height_in_pixels(cx.rem_size())
 2104    });
 2105    let window = cx.window;
 2106    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2107    cx.set_state(
 2108        &r#"
 2109        ˇone
 2110        two
 2111        threeˇ
 2112        four
 2113        five
 2114        six
 2115        seven
 2116        eight
 2117        nine
 2118        ten
 2119        "#
 2120        .unindent(),
 2121    );
 2122
 2123    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2124    cx.assert_editor_state(
 2125        &r#"
 2126        one
 2127        two
 2128        three
 2129        ˇfour
 2130        five
 2131        sixˇ
 2132        seven
 2133        eight
 2134        nine
 2135        ten
 2136        "#
 2137        .unindent(),
 2138    );
 2139
 2140    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2141    cx.assert_editor_state(
 2142        &r#"
 2143        one
 2144        two
 2145        three
 2146        four
 2147        five
 2148        six
 2149        ˇseven
 2150        eight
 2151        nineˇ
 2152        ten
 2153        "#
 2154        .unindent(),
 2155    );
 2156
 2157    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2158    cx.assert_editor_state(
 2159        &r#"
 2160        one
 2161        two
 2162        three
 2163        ˇfour
 2164        five
 2165        sixˇ
 2166        seven
 2167        eight
 2168        nine
 2169        ten
 2170        "#
 2171        .unindent(),
 2172    );
 2173
 2174    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2175    cx.assert_editor_state(
 2176        &r#"
 2177        ˇone
 2178        two
 2179        threeˇ
 2180        four
 2181        five
 2182        six
 2183        seven
 2184        eight
 2185        nine
 2186        ten
 2187        "#
 2188        .unindent(),
 2189    );
 2190
 2191    // Test select collapsing
 2192    cx.update_editor(|editor, cx| {
 2193        editor.move_page_down(&MovePageDown::default(), cx);
 2194        editor.move_page_down(&MovePageDown::default(), cx);
 2195        editor.move_page_down(&MovePageDown::default(), cx);
 2196    });
 2197    cx.assert_editor_state(
 2198        &r#"
 2199        one
 2200        two
 2201        three
 2202        four
 2203        five
 2204        six
 2205        seven
 2206        eight
 2207        nine
 2208        ˇten
 2209        ˇ"#
 2210        .unindent(),
 2211    );
 2212}
 2213
 2214#[gpui::test]
 2215async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2216    init_test(cx, |_| {});
 2217    let mut cx = EditorTestContext::new(cx).await;
 2218    cx.set_state("one «two threeˇ» four");
 2219    cx.update_editor(|editor, cx| {
 2220        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2221        assert_eq!(editor.text(cx), " four");
 2222    });
 2223}
 2224
 2225#[gpui::test]
 2226fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2227    init_test(cx, |_| {});
 2228
 2229    let view = cx.add_window(|cx| {
 2230        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2231        build_editor(buffer.clone(), cx)
 2232    });
 2233
 2234    _ = view.update(cx, |view, cx| {
 2235        view.change_selections(None, cx, |s| {
 2236            s.select_display_ranges([
 2237                // an empty selection - the preceding word fragment is deleted
 2238                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2239                // characters selected - they are deleted
 2240                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2241            ])
 2242        });
 2243        view.delete_to_previous_word_start(
 2244            &DeleteToPreviousWordStart {
 2245                ignore_newlines: false,
 2246            },
 2247            cx,
 2248        );
 2249        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2250    });
 2251
 2252    _ = view.update(cx, |view, cx| {
 2253        view.change_selections(None, cx, |s| {
 2254            s.select_display_ranges([
 2255                // an empty selection - the following word fragment is deleted
 2256                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2257                // characters selected - they are deleted
 2258                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2259            ])
 2260        });
 2261        view.delete_to_next_word_end(
 2262            &DeleteToNextWordEnd {
 2263                ignore_newlines: false,
 2264            },
 2265            cx,
 2266        );
 2267        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2268    });
 2269}
 2270
 2271#[gpui::test]
 2272fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2273    init_test(cx, |_| {});
 2274
 2275    let view = cx.add_window(|cx| {
 2276        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2277        build_editor(buffer.clone(), cx)
 2278    });
 2279    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2280        ignore_newlines: false,
 2281    };
 2282    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2283        ignore_newlines: true,
 2284    };
 2285
 2286    _ = view.update(cx, |view, cx| {
 2287        view.change_selections(None, cx, |s| {
 2288            s.select_display_ranges([
 2289                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2290            ])
 2291        });
 2292        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2293        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2294        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2295        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2296        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2297        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2298        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2299        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2300        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2301        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2302        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2303        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2304    });
 2305}
 2306
 2307#[gpui::test]
 2308fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2309    init_test(cx, |_| {});
 2310
 2311    let view = cx.add_window(|cx| {
 2312        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2313        build_editor(buffer.clone(), cx)
 2314    });
 2315    let del_to_next_word_end = DeleteToNextWordEnd {
 2316        ignore_newlines: false,
 2317    };
 2318    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2319        ignore_newlines: true,
 2320    };
 2321
 2322    _ = view.update(cx, |view, cx| {
 2323        view.change_selections(None, cx, |s| {
 2324            s.select_display_ranges([
 2325                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2326            ])
 2327        });
 2328        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2329        assert_eq!(
 2330            view.buffer.read(cx).read(cx).text(),
 2331            "one\n   two\nthree\n   four"
 2332        );
 2333        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2334        assert_eq!(
 2335            view.buffer.read(cx).read(cx).text(),
 2336            "\n   two\nthree\n   four"
 2337        );
 2338        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2339        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2340        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2341        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2342        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2343        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2344        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2345        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2346    });
 2347}
 2348
 2349#[gpui::test]
 2350fn test_newline(cx: &mut TestAppContext) {
 2351    init_test(cx, |_| {});
 2352
 2353    let view = cx.add_window(|cx| {
 2354        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2355        build_editor(buffer.clone(), cx)
 2356    });
 2357
 2358    _ = view.update(cx, |view, cx| {
 2359        view.change_selections(None, cx, |s| {
 2360            s.select_display_ranges([
 2361                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2362                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2363                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2364            ])
 2365        });
 2366
 2367        view.newline(&Newline, cx);
 2368        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2369    });
 2370}
 2371
 2372#[gpui::test]
 2373fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2374    init_test(cx, |_| {});
 2375
 2376    let editor = cx.add_window(|cx| {
 2377        let buffer = MultiBuffer::build_simple(
 2378            "
 2379                a
 2380                b(
 2381                    X
 2382                )
 2383                c(
 2384                    X
 2385                )
 2386            "
 2387            .unindent()
 2388            .as_str(),
 2389            cx,
 2390        );
 2391        let mut editor = build_editor(buffer.clone(), cx);
 2392        editor.change_selections(None, cx, |s| {
 2393            s.select_ranges([
 2394                Point::new(2, 4)..Point::new(2, 5),
 2395                Point::new(5, 4)..Point::new(5, 5),
 2396            ])
 2397        });
 2398        editor
 2399    });
 2400
 2401    _ = editor.update(cx, |editor, cx| {
 2402        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2403        editor.buffer.update(cx, |buffer, cx| {
 2404            buffer.edit(
 2405                [
 2406                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2407                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2408                ],
 2409                None,
 2410                cx,
 2411            );
 2412            assert_eq!(
 2413                buffer.read(cx).text(),
 2414                "
 2415                    a
 2416                    b()
 2417                    c()
 2418                "
 2419                .unindent()
 2420            );
 2421        });
 2422        assert_eq!(
 2423            editor.selections.ranges(cx),
 2424            &[
 2425                Point::new(1, 2)..Point::new(1, 2),
 2426                Point::new(2, 2)..Point::new(2, 2),
 2427            ],
 2428        );
 2429
 2430        editor.newline(&Newline, cx);
 2431        assert_eq!(
 2432            editor.text(cx),
 2433            "
 2434                a
 2435                b(
 2436                )
 2437                c(
 2438                )
 2439            "
 2440            .unindent()
 2441        );
 2442
 2443        // The selections are moved after the inserted newlines
 2444        assert_eq!(
 2445            editor.selections.ranges(cx),
 2446            &[
 2447                Point::new(2, 0)..Point::new(2, 0),
 2448                Point::new(4, 0)..Point::new(4, 0),
 2449            ],
 2450        );
 2451    });
 2452}
 2453
 2454#[gpui::test]
 2455async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2456    init_test(cx, |settings| {
 2457        settings.defaults.tab_size = NonZeroU32::new(4)
 2458    });
 2459
 2460    let language = Arc::new(
 2461        Language::new(
 2462            LanguageConfig::default(),
 2463            Some(tree_sitter_rust::LANGUAGE.into()),
 2464        )
 2465        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2466        .unwrap(),
 2467    );
 2468
 2469    let mut cx = EditorTestContext::new(cx).await;
 2470    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2471    cx.set_state(indoc! {"
 2472        const a: ˇA = (
 2473 2474                «const_functionˇ»(ˇ),
 2475                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2476 2477        ˇ);ˇ
 2478    "});
 2479
 2480    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2481    cx.assert_editor_state(indoc! {"
 2482        ˇ
 2483        const a: A = (
 2484            ˇ
 2485            (
 2486                ˇ
 2487                ˇ
 2488                const_function(),
 2489                ˇ
 2490                ˇ
 2491                ˇ
 2492                ˇ
 2493                something_else,
 2494                ˇ
 2495            )
 2496            ˇ
 2497            ˇ
 2498        );
 2499    "});
 2500}
 2501
 2502#[gpui::test]
 2503async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2504    init_test(cx, |settings| {
 2505        settings.defaults.tab_size = NonZeroU32::new(4)
 2506    });
 2507
 2508    let language = Arc::new(
 2509        Language::new(
 2510            LanguageConfig::default(),
 2511            Some(tree_sitter_rust::LANGUAGE.into()),
 2512        )
 2513        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2514        .unwrap(),
 2515    );
 2516
 2517    let mut cx = EditorTestContext::new(cx).await;
 2518    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2519    cx.set_state(indoc! {"
 2520        const a: ˇA = (
 2521 2522                «const_functionˇ»(ˇ),
 2523                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2524 2525        ˇ);ˇ
 2526    "});
 2527
 2528    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2529    cx.assert_editor_state(indoc! {"
 2530        const a: A = (
 2531            ˇ
 2532            (
 2533                ˇ
 2534                const_function(),
 2535                ˇ
 2536                ˇ
 2537                something_else,
 2538                ˇ
 2539                ˇ
 2540                ˇ
 2541                ˇ
 2542            )
 2543            ˇ
 2544        );
 2545        ˇ
 2546        ˇ
 2547    "});
 2548}
 2549
 2550#[gpui::test]
 2551async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2552    init_test(cx, |settings| {
 2553        settings.defaults.tab_size = NonZeroU32::new(4)
 2554    });
 2555
 2556    let language = Arc::new(Language::new(
 2557        LanguageConfig {
 2558            line_comments: vec!["//".into()],
 2559            ..LanguageConfig::default()
 2560        },
 2561        None,
 2562    ));
 2563    {
 2564        let mut cx = EditorTestContext::new(cx).await;
 2565        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2566        cx.set_state(indoc! {"
 2567        // Fooˇ
 2568    "});
 2569
 2570        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2571        cx.assert_editor_state(indoc! {"
 2572        // Foo
 2573        //ˇ
 2574    "});
 2575        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2576        cx.set_state(indoc! {"
 2577        ˇ// Foo
 2578    "});
 2579        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2580        cx.assert_editor_state(indoc! {"
 2581
 2582        ˇ// Foo
 2583    "});
 2584    }
 2585    // Ensure that comment continuations can be disabled.
 2586    update_test_language_settings(cx, |settings| {
 2587        settings.defaults.extend_comment_on_newline = Some(false);
 2588    });
 2589    let mut cx = EditorTestContext::new(cx).await;
 2590    cx.set_state(indoc! {"
 2591        // Fooˇ
 2592    "});
 2593    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2594    cx.assert_editor_state(indoc! {"
 2595        // Foo
 2596        ˇ
 2597    "});
 2598}
 2599
 2600#[gpui::test]
 2601fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2602    init_test(cx, |_| {});
 2603
 2604    let editor = cx.add_window(|cx| {
 2605        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2606        let mut editor = build_editor(buffer.clone(), cx);
 2607        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2608        editor
 2609    });
 2610
 2611    _ = editor.update(cx, |editor, cx| {
 2612        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2613        editor.buffer.update(cx, |buffer, cx| {
 2614            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2615            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2616        });
 2617        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2618
 2619        editor.insert("Z", cx);
 2620        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2621
 2622        // The selections are moved after the inserted characters
 2623        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2624    });
 2625}
 2626
 2627#[gpui::test]
 2628async fn test_tab(cx: &mut gpui::TestAppContext) {
 2629    init_test(cx, |settings| {
 2630        settings.defaults.tab_size = NonZeroU32::new(3)
 2631    });
 2632
 2633    let mut cx = EditorTestContext::new(cx).await;
 2634    cx.set_state(indoc! {"
 2635        ˇabˇc
 2636        ˇ🏀ˇ🏀ˇefg
 2637 2638    "});
 2639    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2640    cx.assert_editor_state(indoc! {"
 2641           ˇab ˇc
 2642           ˇ🏀  ˇ🏀  ˇefg
 2643        d  ˇ
 2644    "});
 2645
 2646    cx.set_state(indoc! {"
 2647        a
 2648        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        a
 2653           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2654    "});
 2655}
 2656
 2657#[gpui::test]
 2658async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2659    init_test(cx, |_| {});
 2660
 2661    let mut cx = EditorTestContext::new(cx).await;
 2662    let language = Arc::new(
 2663        Language::new(
 2664            LanguageConfig::default(),
 2665            Some(tree_sitter_rust::LANGUAGE.into()),
 2666        )
 2667        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2668        .unwrap(),
 2669    );
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671
 2672    // cursors that are already at the suggested indent level insert
 2673    // a soft tab. cursors that are to the left of the suggested indent
 2674    // auto-indent their line.
 2675    cx.set_state(indoc! {"
 2676        ˇ
 2677        const a: B = (
 2678            c(
 2679                d(
 2680        ˇ
 2681                )
 2682        ˇ
 2683        ˇ    )
 2684        );
 2685    "});
 2686    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2687    cx.assert_editor_state(indoc! {"
 2688            ˇ
 2689        const a: B = (
 2690            c(
 2691                d(
 2692                    ˇ
 2693                )
 2694                ˇ
 2695            ˇ)
 2696        );
 2697    "});
 2698
 2699    // handle auto-indent when there are multiple cursors on the same line
 2700    cx.set_state(indoc! {"
 2701        const a: B = (
 2702            c(
 2703        ˇ    ˇ
 2704        ˇ    )
 2705        );
 2706    "});
 2707    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2708    cx.assert_editor_state(indoc! {"
 2709        const a: B = (
 2710            c(
 2711                ˇ
 2712            ˇ)
 2713        );
 2714    "});
 2715}
 2716
 2717#[gpui::test]
 2718async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2719    init_test(cx, |settings| {
 2720        settings.defaults.tab_size = NonZeroU32::new(4)
 2721    });
 2722
 2723    let language = Arc::new(
 2724        Language::new(
 2725            LanguageConfig::default(),
 2726            Some(tree_sitter_rust::LANGUAGE.into()),
 2727        )
 2728        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2729        .unwrap(),
 2730    );
 2731
 2732    let mut cx = EditorTestContext::new(cx).await;
 2733    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2734    cx.set_state(indoc! {"
 2735        fn a() {
 2736            if b {
 2737        \t ˇc
 2738            }
 2739        }
 2740    "});
 2741
 2742    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2743    cx.assert_editor_state(indoc! {"
 2744        fn a() {
 2745            if b {
 2746                ˇc
 2747            }
 2748        }
 2749    "});
 2750}
 2751
 2752#[gpui::test]
 2753async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2754    init_test(cx, |settings| {
 2755        settings.defaults.tab_size = NonZeroU32::new(4);
 2756    });
 2757
 2758    let mut cx = EditorTestContext::new(cx).await;
 2759
 2760    cx.set_state(indoc! {"
 2761          «oneˇ» «twoˇ»
 2762        three
 2763         four
 2764    "});
 2765    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2766    cx.assert_editor_state(indoc! {"
 2767            «oneˇ» «twoˇ»
 2768        three
 2769         four
 2770    "});
 2771
 2772    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2773    cx.assert_editor_state(indoc! {"
 2774        «oneˇ» «twoˇ»
 2775        three
 2776         four
 2777    "});
 2778
 2779    // select across line ending
 2780    cx.set_state(indoc! {"
 2781        one two
 2782        t«hree
 2783        ˇ» four
 2784    "});
 2785    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2786    cx.assert_editor_state(indoc! {"
 2787        one two
 2788            t«hree
 2789        ˇ» four
 2790    "});
 2791
 2792    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        one two
 2795        t«hree
 2796        ˇ» four
 2797    "});
 2798
 2799    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2800    cx.set_state(indoc! {"
 2801        one two
 2802        ˇthree
 2803            four
 2804    "});
 2805    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2806    cx.assert_editor_state(indoc! {"
 2807        one two
 2808            ˇthree
 2809            four
 2810    "});
 2811
 2812    cx.set_state(indoc! {"
 2813        one two
 2814        ˇ    three
 2815            four
 2816    "});
 2817    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2818    cx.assert_editor_state(indoc! {"
 2819        one two
 2820        ˇthree
 2821            four
 2822    "});
 2823}
 2824
 2825#[gpui::test]
 2826async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2827    init_test(cx, |settings| {
 2828        settings.defaults.hard_tabs = Some(true);
 2829    });
 2830
 2831    let mut cx = EditorTestContext::new(cx).await;
 2832
 2833    // select two ranges on one line
 2834    cx.set_state(indoc! {"
 2835        «oneˇ» «twoˇ»
 2836        three
 2837        four
 2838    "});
 2839    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2840    cx.assert_editor_state(indoc! {"
 2841        \t«oneˇ» «twoˇ»
 2842        three
 2843        four
 2844    "});
 2845    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2846    cx.assert_editor_state(indoc! {"
 2847        \t\t«oneˇ» «twoˇ»
 2848        three
 2849        four
 2850    "});
 2851    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        \t«oneˇ» «twoˇ»
 2854        three
 2855        four
 2856    "});
 2857    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2858    cx.assert_editor_state(indoc! {"
 2859        «oneˇ» «twoˇ»
 2860        three
 2861        four
 2862    "});
 2863
 2864    // select across a line ending
 2865    cx.set_state(indoc! {"
 2866        one two
 2867        t«hree
 2868        ˇ»four
 2869    "});
 2870    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2871    cx.assert_editor_state(indoc! {"
 2872        one two
 2873        \tt«hree
 2874        ˇ»four
 2875    "});
 2876    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2877    cx.assert_editor_state(indoc! {"
 2878        one two
 2879        \t\tt«hree
 2880        ˇ»four
 2881    "});
 2882    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884        one two
 2885        \tt«hree
 2886        ˇ»four
 2887    "});
 2888    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2889    cx.assert_editor_state(indoc! {"
 2890        one two
 2891        t«hree
 2892        ˇ»four
 2893    "});
 2894
 2895    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2896    cx.set_state(indoc! {"
 2897        one two
 2898        ˇthree
 2899        four
 2900    "});
 2901    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2902    cx.assert_editor_state(indoc! {"
 2903        one two
 2904        ˇthree
 2905        four
 2906    "});
 2907    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2908    cx.assert_editor_state(indoc! {"
 2909        one two
 2910        \tˇthree
 2911        four
 2912    "});
 2913    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2914    cx.assert_editor_state(indoc! {"
 2915        one two
 2916        ˇthree
 2917        four
 2918    "});
 2919}
 2920
 2921#[gpui::test]
 2922fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2923    init_test(cx, |settings| {
 2924        settings.languages.extend([
 2925            (
 2926                "TOML".into(),
 2927                LanguageSettingsContent {
 2928                    tab_size: NonZeroU32::new(2),
 2929                    ..Default::default()
 2930                },
 2931            ),
 2932            (
 2933                "Rust".into(),
 2934                LanguageSettingsContent {
 2935                    tab_size: NonZeroU32::new(4),
 2936                    ..Default::default()
 2937                },
 2938            ),
 2939        ]);
 2940    });
 2941
 2942    let toml_language = Arc::new(Language::new(
 2943        LanguageConfig {
 2944            name: "TOML".into(),
 2945            ..Default::default()
 2946        },
 2947        None,
 2948    ));
 2949    let rust_language = Arc::new(Language::new(
 2950        LanguageConfig {
 2951            name: "Rust".into(),
 2952            ..Default::default()
 2953        },
 2954        None,
 2955    ));
 2956
 2957    let toml_buffer =
 2958        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2959    let rust_buffer = cx.new_model(|cx| {
 2960        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2961    });
 2962    let multibuffer = cx.new_model(|cx| {
 2963        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2964        multibuffer.push_excerpts(
 2965            toml_buffer.clone(),
 2966            [ExcerptRange {
 2967                context: Point::new(0, 0)..Point::new(2, 0),
 2968                primary: None,
 2969            }],
 2970            cx,
 2971        );
 2972        multibuffer.push_excerpts(
 2973            rust_buffer.clone(),
 2974            [ExcerptRange {
 2975                context: Point::new(0, 0)..Point::new(1, 0),
 2976                primary: None,
 2977            }],
 2978            cx,
 2979        );
 2980        multibuffer
 2981    });
 2982
 2983    cx.add_window(|cx| {
 2984        let mut editor = build_editor(multibuffer, cx);
 2985
 2986        assert_eq!(
 2987            editor.text(cx),
 2988            indoc! {"
 2989                a = 1
 2990                b = 2
 2991
 2992                const c: usize = 3;
 2993            "}
 2994        );
 2995
 2996        select_ranges(
 2997            &mut editor,
 2998            indoc! {"
 2999                «aˇ» = 1
 3000                b = 2
 3001
 3002                «const c:ˇ» usize = 3;
 3003            "},
 3004            cx,
 3005        );
 3006
 3007        editor.tab(&Tab, cx);
 3008        assert_text_with_selections(
 3009            &mut editor,
 3010            indoc! {"
 3011                  «aˇ» = 1
 3012                b = 2
 3013
 3014                    «const c:ˇ» usize = 3;
 3015            "},
 3016            cx,
 3017        );
 3018        editor.tab_prev(&TabPrev, cx);
 3019        assert_text_with_selections(
 3020            &mut editor,
 3021            indoc! {"
 3022                «aˇ» = 1
 3023                b = 2
 3024
 3025                «const c:ˇ» usize = 3;
 3026            "},
 3027            cx,
 3028        );
 3029
 3030        editor
 3031    });
 3032}
 3033
 3034#[gpui::test]
 3035async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3036    init_test(cx, |_| {});
 3037
 3038    let mut cx = EditorTestContext::new(cx).await;
 3039
 3040    // Basic backspace
 3041    cx.set_state(indoc! {"
 3042        onˇe two three
 3043        fou«rˇ» five six
 3044        seven «ˇeight nine
 3045        »ten
 3046    "});
 3047    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3048    cx.assert_editor_state(indoc! {"
 3049        oˇe two three
 3050        fouˇ five six
 3051        seven ˇten
 3052    "});
 3053
 3054    // Test backspace inside and around indents
 3055    cx.set_state(indoc! {"
 3056        zero
 3057            ˇone
 3058                ˇtwo
 3059            ˇ ˇ ˇ  three
 3060        ˇ  ˇ  four
 3061    "});
 3062    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3063    cx.assert_editor_state(indoc! {"
 3064        zero
 3065        ˇone
 3066            ˇtwo
 3067        ˇ  threeˇ  four
 3068    "});
 3069
 3070    // Test backspace with line_mode set to true
 3071    cx.update_editor(|e, _| e.selections.line_mode = true);
 3072    cx.set_state(indoc! {"
 3073        The ˇquick ˇbrown
 3074        fox jumps over
 3075        the lazy dog
 3076        ˇThe qu«ick bˇ»rown"});
 3077    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        ˇfox jumps over
 3080        the lazy dogˇ"});
 3081}
 3082
 3083#[gpui::test]
 3084async fn test_delete(cx: &mut gpui::TestAppContext) {
 3085    init_test(cx, |_| {});
 3086
 3087    let mut cx = EditorTestContext::new(cx).await;
 3088    cx.set_state(indoc! {"
 3089        onˇe two three
 3090        fou«rˇ» five six
 3091        seven «ˇeight nine
 3092        »ten
 3093    "});
 3094    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3095    cx.assert_editor_state(indoc! {"
 3096        onˇ two three
 3097        fouˇ five six
 3098        seven ˇten
 3099    "});
 3100
 3101    // Test backspace with line_mode set to true
 3102    cx.update_editor(|e, _| e.selections.line_mode = true);
 3103    cx.set_state(indoc! {"
 3104        The ˇquick ˇbrown
 3105        fox «ˇjum»ps over
 3106        the lazy dog
 3107        ˇThe qu«ick bˇ»rown"});
 3108    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3109    cx.assert_editor_state("ˇthe lazy dogˇ");
 3110}
 3111
 3112#[gpui::test]
 3113fn test_delete_line(cx: &mut TestAppContext) {
 3114    init_test(cx, |_| {});
 3115
 3116    let view = cx.add_window(|cx| {
 3117        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3118        build_editor(buffer, cx)
 3119    });
 3120    _ = view.update(cx, |view, cx| {
 3121        view.change_selections(None, cx, |s| {
 3122            s.select_display_ranges([
 3123                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3125                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3126            ])
 3127        });
 3128        view.delete_line(&DeleteLine, cx);
 3129        assert_eq!(view.display_text(cx), "ghi");
 3130        assert_eq!(
 3131            view.selections.display_ranges(cx),
 3132            vec![
 3133                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3134                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3135            ]
 3136        );
 3137    });
 3138
 3139    let view = cx.add_window(|cx| {
 3140        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3141        build_editor(buffer, cx)
 3142    });
 3143    _ = view.update(cx, |view, cx| {
 3144        view.change_selections(None, cx, |s| {
 3145            s.select_display_ranges([
 3146                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3147            ])
 3148        });
 3149        view.delete_line(&DeleteLine, cx);
 3150        assert_eq!(view.display_text(cx), "ghi\n");
 3151        assert_eq!(
 3152            view.selections.display_ranges(cx),
 3153            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3154        );
 3155    });
 3156}
 3157
 3158#[gpui::test]
 3159fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3160    init_test(cx, |_| {});
 3161
 3162    cx.add_window(|cx| {
 3163        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3164        let mut editor = build_editor(buffer.clone(), cx);
 3165        let buffer = buffer.read(cx).as_singleton().unwrap();
 3166
 3167        assert_eq!(
 3168            editor.selections.ranges::<Point>(cx),
 3169            &[Point::new(0, 0)..Point::new(0, 0)]
 3170        );
 3171
 3172        // When on single line, replace newline at end by space
 3173        editor.join_lines(&JoinLines, cx);
 3174        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3175        assert_eq!(
 3176            editor.selections.ranges::<Point>(cx),
 3177            &[Point::new(0, 3)..Point::new(0, 3)]
 3178        );
 3179
 3180        // When multiple lines are selected, remove newlines that are spanned by the selection
 3181        editor.change_selections(None, cx, |s| {
 3182            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3183        });
 3184        editor.join_lines(&JoinLines, cx);
 3185        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3186        assert_eq!(
 3187            editor.selections.ranges::<Point>(cx),
 3188            &[Point::new(0, 11)..Point::new(0, 11)]
 3189        );
 3190
 3191        // Undo should be transactional
 3192        editor.undo(&Undo, cx);
 3193        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3194        assert_eq!(
 3195            editor.selections.ranges::<Point>(cx),
 3196            &[Point::new(0, 5)..Point::new(2, 2)]
 3197        );
 3198
 3199        // When joining an empty line don't insert a space
 3200        editor.change_selections(None, cx, |s| {
 3201            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3202        });
 3203        editor.join_lines(&JoinLines, cx);
 3204        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3205        assert_eq!(
 3206            editor.selections.ranges::<Point>(cx),
 3207            [Point::new(2, 3)..Point::new(2, 3)]
 3208        );
 3209
 3210        // We can remove trailing newlines
 3211        editor.join_lines(&JoinLines, cx);
 3212        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3213        assert_eq!(
 3214            editor.selections.ranges::<Point>(cx),
 3215            [Point::new(2, 3)..Point::new(2, 3)]
 3216        );
 3217
 3218        // We don't blow up on the last line
 3219        editor.join_lines(&JoinLines, cx);
 3220        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3221        assert_eq!(
 3222            editor.selections.ranges::<Point>(cx),
 3223            [Point::new(2, 3)..Point::new(2, 3)]
 3224        );
 3225
 3226        // reset to test indentation
 3227        editor.buffer.update(cx, |buffer, cx| {
 3228            buffer.edit(
 3229                [
 3230                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3231                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3232                ],
 3233                None,
 3234                cx,
 3235            )
 3236        });
 3237
 3238        // We remove any leading spaces
 3239        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3240        editor.change_selections(None, cx, |s| {
 3241            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3242        });
 3243        editor.join_lines(&JoinLines, cx);
 3244        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3245
 3246        // We don't insert a space for a line containing only spaces
 3247        editor.join_lines(&JoinLines, cx);
 3248        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3249
 3250        // We ignore any leading tabs
 3251        editor.join_lines(&JoinLines, cx);
 3252        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3253
 3254        editor
 3255    });
 3256}
 3257
 3258#[gpui::test]
 3259fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3260    init_test(cx, |_| {});
 3261
 3262    cx.add_window(|cx| {
 3263        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3264        let mut editor = build_editor(buffer.clone(), cx);
 3265        let buffer = buffer.read(cx).as_singleton().unwrap();
 3266
 3267        editor.change_selections(None, cx, |s| {
 3268            s.select_ranges([
 3269                Point::new(0, 2)..Point::new(1, 1),
 3270                Point::new(1, 2)..Point::new(1, 2),
 3271                Point::new(3, 1)..Point::new(3, 2),
 3272            ])
 3273        });
 3274
 3275        editor.join_lines(&JoinLines, cx);
 3276        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3277
 3278        assert_eq!(
 3279            editor.selections.ranges::<Point>(cx),
 3280            [
 3281                Point::new(0, 7)..Point::new(0, 7),
 3282                Point::new(1, 3)..Point::new(1, 3)
 3283            ]
 3284        );
 3285        editor
 3286    });
 3287}
 3288
 3289#[gpui::test]
 3290async fn test_join_lines_with_git_diff_base(
 3291    executor: BackgroundExecutor,
 3292    cx: &mut gpui::TestAppContext,
 3293) {
 3294    init_test(cx, |_| {});
 3295
 3296    let mut cx = EditorTestContext::new(cx).await;
 3297
 3298    let diff_base = r#"
 3299        Line 0
 3300        Line 1
 3301        Line 2
 3302        Line 3
 3303        "#
 3304    .unindent();
 3305
 3306    cx.set_state(
 3307        &r#"
 3308        ˇLine 0
 3309        Line 1
 3310        Line 2
 3311        Line 3
 3312        "#
 3313        .unindent(),
 3314    );
 3315
 3316    cx.set_diff_base(&diff_base);
 3317    executor.run_until_parked();
 3318
 3319    // Join lines
 3320    cx.update_editor(|editor, cx| {
 3321        editor.join_lines(&JoinLines, cx);
 3322    });
 3323    executor.run_until_parked();
 3324
 3325    cx.assert_editor_state(
 3326        &r#"
 3327        Line 0ˇ Line 1
 3328        Line 2
 3329        Line 3
 3330        "#
 3331        .unindent(),
 3332    );
 3333    // Join again
 3334    cx.update_editor(|editor, cx| {
 3335        editor.join_lines(&JoinLines, cx);
 3336    });
 3337    executor.run_until_parked();
 3338
 3339    cx.assert_editor_state(
 3340        &r#"
 3341        Line 0 Line 1ˇ Line 2
 3342        Line 3
 3343        "#
 3344        .unindent(),
 3345    );
 3346}
 3347
 3348#[gpui::test]
 3349async fn test_custom_newlines_cause_no_false_positive_diffs(
 3350    executor: BackgroundExecutor,
 3351    cx: &mut gpui::TestAppContext,
 3352) {
 3353    init_test(cx, |_| {});
 3354    let mut cx = EditorTestContext::new(cx).await;
 3355    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3356    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3357    executor.run_until_parked();
 3358
 3359    cx.update_editor(|editor, cx| {
 3360        let snapshot = editor.snapshot(cx);
 3361        assert_eq!(
 3362            snapshot
 3363                .diff_map
 3364                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
 3365                .collect::<Vec<_>>(),
 3366            Vec::new(),
 3367            "Should not have any diffs for files with custom newlines"
 3368        );
 3369    });
 3370}
 3371
 3372#[gpui::test]
 3373async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3374    init_test(cx, |_| {});
 3375
 3376    let mut cx = EditorTestContext::new(cx).await;
 3377
 3378    // Test sort_lines_case_insensitive()
 3379    cx.set_state(indoc! {"
 3380        «z
 3381        y
 3382        x
 3383        Z
 3384        Y
 3385        Xˇ»
 3386    "});
 3387    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3388    cx.assert_editor_state(indoc! {"
 3389        «x
 3390        X
 3391        y
 3392        Y
 3393        z
 3394        Zˇ»
 3395    "});
 3396
 3397    // Test reverse_lines()
 3398    cx.set_state(indoc! {"
 3399        «5
 3400        4
 3401        3
 3402        2
 3403        1ˇ»
 3404    "});
 3405    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3406    cx.assert_editor_state(indoc! {"
 3407        «1
 3408        2
 3409        3
 3410        4
 3411        5ˇ»
 3412    "});
 3413
 3414    // Skip testing shuffle_line()
 3415
 3416    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3417    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3418
 3419    // Don't manipulate when cursor is on single line, but expand the selection
 3420    cx.set_state(indoc! {"
 3421        ddˇdd
 3422        ccc
 3423        bb
 3424        a
 3425    "});
 3426    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3427    cx.assert_editor_state(indoc! {"
 3428        «ddddˇ»
 3429        ccc
 3430        bb
 3431        a
 3432    "});
 3433
 3434    // Basic manipulate case
 3435    // Start selection moves to column 0
 3436    // End of selection shrinks to fit shorter line
 3437    cx.set_state(indoc! {"
 3438        dd«d
 3439        ccc
 3440        bb
 3441        aaaaaˇ»
 3442    "});
 3443    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3444    cx.assert_editor_state(indoc! {"
 3445        «aaaaa
 3446        bb
 3447        ccc
 3448        dddˇ»
 3449    "});
 3450
 3451    // Manipulate case with newlines
 3452    cx.set_state(indoc! {"
 3453        dd«d
 3454        ccc
 3455
 3456        bb
 3457        aaaaa
 3458
 3459        ˇ»
 3460    "});
 3461    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3462    cx.assert_editor_state(indoc! {"
 3463        «
 3464
 3465        aaaaa
 3466        bb
 3467        ccc
 3468        dddˇ»
 3469
 3470    "});
 3471
 3472    // Adding new line
 3473    cx.set_state(indoc! {"
 3474        aa«a
 3475        bbˇ»b
 3476    "});
 3477    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3478    cx.assert_editor_state(indoc! {"
 3479        «aaa
 3480        bbb
 3481        added_lineˇ»
 3482    "});
 3483
 3484    // Removing line
 3485    cx.set_state(indoc! {"
 3486        aa«a
 3487        bbbˇ»
 3488    "});
 3489    cx.update_editor(|e, cx| {
 3490        e.manipulate_lines(cx, |lines| {
 3491            lines.pop();
 3492        })
 3493    });
 3494    cx.assert_editor_state(indoc! {"
 3495        «aaaˇ»
 3496    "});
 3497
 3498    // Removing all lines
 3499    cx.set_state(indoc! {"
 3500        aa«a
 3501        bbbˇ»
 3502    "});
 3503    cx.update_editor(|e, cx| {
 3504        e.manipulate_lines(cx, |lines| {
 3505            lines.drain(..);
 3506        })
 3507    });
 3508    cx.assert_editor_state(indoc! {"
 3509        ˇ
 3510    "});
 3511}
 3512
 3513#[gpui::test]
 3514async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3515    init_test(cx, |_| {});
 3516
 3517    let mut cx = EditorTestContext::new(cx).await;
 3518
 3519    // Consider continuous selection as single selection
 3520    cx.set_state(indoc! {"
 3521        Aaa«aa
 3522        cˇ»c«c
 3523        bb
 3524        aaaˇ»aa
 3525    "});
 3526    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3527    cx.assert_editor_state(indoc! {"
 3528        «Aaaaa
 3529        ccc
 3530        bb
 3531        aaaaaˇ»
 3532    "});
 3533
 3534    cx.set_state(indoc! {"
 3535        Aaa«aa
 3536        cˇ»c«c
 3537        bb
 3538        aaaˇ»aa
 3539    "});
 3540    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3541    cx.assert_editor_state(indoc! {"
 3542        «Aaaaa
 3543        ccc
 3544        bbˇ»
 3545    "});
 3546
 3547    // Consider non continuous selection as distinct dedup operations
 3548    cx.set_state(indoc! {"
 3549        «aaaaa
 3550        bb
 3551        aaaaa
 3552        aaaaaˇ»
 3553
 3554        aaa«aaˇ»
 3555    "});
 3556    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3557    cx.assert_editor_state(indoc! {"
 3558        «aaaaa
 3559        bbˇ»
 3560
 3561        «aaaaaˇ»
 3562    "});
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    cx.set_state(indoc! {"
 3572        «Aaa
 3573        aAa
 3574        Aaaˇ»
 3575    "});
 3576    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3577    cx.assert_editor_state(indoc! {"
 3578        «Aaa
 3579        aAaˇ»
 3580    "});
 3581
 3582    cx.set_state(indoc! {"
 3583        «Aaa
 3584        aAa
 3585        aaAˇ»
 3586    "});
 3587    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3588    cx.assert_editor_state(indoc! {"
 3589        «Aaaˇ»
 3590    "});
 3591}
 3592
 3593#[gpui::test]
 3594async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3595    init_test(cx, |_| {});
 3596
 3597    let mut cx = EditorTestContext::new(cx).await;
 3598
 3599    // Manipulate with multiple selections on a single line
 3600    cx.set_state(indoc! {"
 3601        dd«dd
 3602        cˇ»c«c
 3603        bb
 3604        aaaˇ»aa
 3605    "});
 3606    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3607    cx.assert_editor_state(indoc! {"
 3608        «aaaaa
 3609        bb
 3610        ccc
 3611        ddddˇ»
 3612    "});
 3613
 3614    // Manipulate with multiple disjoin selections
 3615    cx.set_state(indoc! {"
 3616 3617        4
 3618        3
 3619        2
 3620        1ˇ»
 3621
 3622        dd«dd
 3623        ccc
 3624        bb
 3625        aaaˇ»aa
 3626    "});
 3627    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3628    cx.assert_editor_state(indoc! {"
 3629        «1
 3630        2
 3631        3
 3632        4
 3633        5ˇ»
 3634
 3635        «aaaaa
 3636        bb
 3637        ccc
 3638        ddddˇ»
 3639    "});
 3640
 3641    // Adding lines on each selection
 3642    cx.set_state(indoc! {"
 3643 3644        1ˇ»
 3645
 3646        bb«bb
 3647        aaaˇ»aa
 3648    "});
 3649    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3650    cx.assert_editor_state(indoc! {"
 3651        «2
 3652        1
 3653        added lineˇ»
 3654
 3655        «bbbb
 3656        aaaaa
 3657        added lineˇ»
 3658    "});
 3659
 3660    // Removing lines on each selection
 3661    cx.set_state(indoc! {"
 3662 3663        1ˇ»
 3664
 3665        bb«bb
 3666        aaaˇ»aa
 3667    "});
 3668    cx.update_editor(|e, cx| {
 3669        e.manipulate_lines(cx, |lines| {
 3670            lines.pop();
 3671        })
 3672    });
 3673    cx.assert_editor_state(indoc! {"
 3674        «2ˇ»
 3675
 3676        «bbbbˇ»
 3677    "});
 3678}
 3679
 3680#[gpui::test]
 3681async fn test_manipulate_text(cx: &mut TestAppContext) {
 3682    init_test(cx, |_| {});
 3683
 3684    let mut cx = EditorTestContext::new(cx).await;
 3685
 3686    // Test convert_to_upper_case()
 3687    cx.set_state(indoc! {"
 3688        «hello worldˇ»
 3689    "});
 3690    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3691    cx.assert_editor_state(indoc! {"
 3692        «HELLO WORLDˇ»
 3693    "});
 3694
 3695    // Test convert_to_lower_case()
 3696    cx.set_state(indoc! {"
 3697        «HELLO WORLDˇ»
 3698    "});
 3699    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3700    cx.assert_editor_state(indoc! {"
 3701        «hello worldˇ»
 3702    "});
 3703
 3704    // Test multiple line, single selection case
 3705    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3706    cx.set_state(indoc! {"
 3707        «The quick brown
 3708        fox jumps over
 3709        the lazy dogˇ»
 3710    "});
 3711    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3712    cx.assert_editor_state(indoc! {"
 3713        «The Quick Brown
 3714        Fox Jumps Over
 3715        The Lazy Dogˇ»
 3716    "});
 3717
 3718    // Test multiple line, single selection case
 3719    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3720    cx.set_state(indoc! {"
 3721        «The quick brown
 3722        fox jumps over
 3723        the lazy dogˇ»
 3724    "});
 3725    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3726    cx.assert_editor_state(indoc! {"
 3727        «TheQuickBrown
 3728        FoxJumpsOver
 3729        TheLazyDogˇ»
 3730    "});
 3731
 3732    // From here on out, test more complex cases of manipulate_text()
 3733
 3734    // Test no selection case - should affect words cursors are in
 3735    // Cursor at beginning, middle, and end of word
 3736    cx.set_state(indoc! {"
 3737        ˇhello big beauˇtiful worldˇ
 3738    "});
 3739    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3740    cx.assert_editor_state(indoc! {"
 3741        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3742    "});
 3743
 3744    // Test multiple selections on a single line and across multiple lines
 3745    cx.set_state(indoc! {"
 3746        «Theˇ» quick «brown
 3747        foxˇ» jumps «overˇ»
 3748        the «lazyˇ» dog
 3749    "});
 3750    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3751    cx.assert_editor_state(indoc! {"
 3752        «THEˇ» quick «BROWN
 3753        FOXˇ» jumps «OVERˇ»
 3754        the «LAZYˇ» dog
 3755    "});
 3756
 3757    // Test case where text length grows
 3758    cx.set_state(indoc! {"
 3759        «tschüߡ»
 3760    "});
 3761    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3762    cx.assert_editor_state(indoc! {"
 3763        «TSCHÜSSˇ»
 3764    "});
 3765
 3766    // Test to make sure we don't crash when text shrinks
 3767    cx.set_state(indoc! {"
 3768        aaa_bbbˇ
 3769    "});
 3770    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3771    cx.assert_editor_state(indoc! {"
 3772        «aaaBbbˇ»
 3773    "});
 3774
 3775    // Test to make sure we all aware of the fact that each word can grow and shrink
 3776    // Final selections should be aware of this fact
 3777    cx.set_state(indoc! {"
 3778        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3779    "});
 3780    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3781    cx.assert_editor_state(indoc! {"
 3782        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3783    "});
 3784
 3785    cx.set_state(indoc! {"
 3786        «hElLo, WoRld!ˇ»
 3787    "});
 3788    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3789    cx.assert_editor_state(indoc! {"
 3790        «HeLlO, wOrLD!ˇ»
 3791    "});
 3792}
 3793
 3794#[gpui::test]
 3795fn test_duplicate_line(cx: &mut TestAppContext) {
 3796    init_test(cx, |_| {});
 3797
 3798    let view = cx.add_window(|cx| {
 3799        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3800        build_editor(buffer, cx)
 3801    });
 3802    _ = view.update(cx, |view, cx| {
 3803        view.change_selections(None, cx, |s| {
 3804            s.select_display_ranges([
 3805                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3806                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3807                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3808                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3809            ])
 3810        });
 3811        view.duplicate_line_down(&DuplicateLineDown, cx);
 3812        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3818                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3819                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3820            ]
 3821        );
 3822    });
 3823
 3824    let view = cx.add_window(|cx| {
 3825        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3826        build_editor(buffer, cx)
 3827    });
 3828    _ = view.update(cx, |view, cx| {
 3829        view.change_selections(None, cx, |s| {
 3830            s.select_display_ranges([
 3831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3833            ])
 3834        });
 3835        view.duplicate_line_down(&DuplicateLineDown, cx);
 3836        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3837        assert_eq!(
 3838            view.selections.display_ranges(cx),
 3839            vec![
 3840                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3841                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3842            ]
 3843        );
 3844    });
 3845
 3846    // With `move_upwards` the selections stay in place, except for
 3847    // the lines inserted above them
 3848    let view = cx.add_window(|cx| {
 3849        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3850        build_editor(buffer, cx)
 3851    });
 3852    _ = view.update(cx, |view, cx| {
 3853        view.change_selections(None, cx, |s| {
 3854            s.select_display_ranges([
 3855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3856                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3857                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3858                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3859            ])
 3860        });
 3861        view.duplicate_line_up(&DuplicateLineUp, cx);
 3862        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3863        assert_eq!(
 3864            view.selections.display_ranges(cx),
 3865            vec![
 3866                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3868                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3869                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3870            ]
 3871        );
 3872    });
 3873
 3874    let view = cx.add_window(|cx| {
 3875        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3876        build_editor(buffer, cx)
 3877    });
 3878    _ = view.update(cx, |view, cx| {
 3879        view.change_selections(None, cx, |s| {
 3880            s.select_display_ranges([
 3881                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3882                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3883            ])
 3884        });
 3885        view.duplicate_line_up(&DuplicateLineUp, cx);
 3886        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3887        assert_eq!(
 3888            view.selections.display_ranges(cx),
 3889            vec![
 3890                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3891                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3892            ]
 3893        );
 3894    });
 3895}
 3896
 3897#[gpui::test]
 3898fn test_move_line_up_down(cx: &mut TestAppContext) {
 3899    init_test(cx, |_| {});
 3900
 3901    let view = cx.add_window(|cx| {
 3902        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3903        build_editor(buffer, cx)
 3904    });
 3905    _ = view.update(cx, |view, cx| {
 3906        view.fold_creases(
 3907            vec![
 3908                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3909                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3910                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3911            ],
 3912            true,
 3913            cx,
 3914        );
 3915        view.change_selections(None, cx, |s| {
 3916            s.select_display_ranges([
 3917                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3918                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3919                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3920                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3921            ])
 3922        });
 3923        assert_eq!(
 3924            view.display_text(cx),
 3925            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3926        );
 3927
 3928        view.move_line_up(&MoveLineUp, cx);
 3929        assert_eq!(
 3930            view.display_text(cx),
 3931            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3932        );
 3933        assert_eq!(
 3934            view.selections.display_ranges(cx),
 3935            vec![
 3936                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3937                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3938                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3939                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3940            ]
 3941        );
 3942    });
 3943
 3944    _ = view.update(cx, |view, cx| {
 3945        view.move_line_down(&MoveLineDown, cx);
 3946        assert_eq!(
 3947            view.display_text(cx),
 3948            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3949        );
 3950        assert_eq!(
 3951            view.selections.display_ranges(cx),
 3952            vec![
 3953                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3955                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3956                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3957            ]
 3958        );
 3959    });
 3960
 3961    _ = view.update(cx, |view, cx| {
 3962        view.move_line_down(&MoveLineDown, cx);
 3963        assert_eq!(
 3964            view.display_text(cx),
 3965            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3966        );
 3967        assert_eq!(
 3968            view.selections.display_ranges(cx),
 3969            vec![
 3970                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3971                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3972                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3973                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3974            ]
 3975        );
 3976    });
 3977
 3978    _ = view.update(cx, |view, cx| {
 3979        view.move_line_up(&MoveLineUp, cx);
 3980        assert_eq!(
 3981            view.display_text(cx),
 3982            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3983        );
 3984        assert_eq!(
 3985            view.selections.display_ranges(cx),
 3986            vec![
 3987                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3988                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3989                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3990                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3991            ]
 3992        );
 3993    });
 3994}
 3995
 3996#[gpui::test]
 3997fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3998    init_test(cx, |_| {});
 3999
 4000    let editor = cx.add_window(|cx| {
 4001        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4002        build_editor(buffer, cx)
 4003    });
 4004    _ = editor.update(cx, |editor, cx| {
 4005        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4006        editor.insert_blocks(
 4007            [BlockProperties {
 4008                style: BlockStyle::Fixed,
 4009                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4010                height: 1,
 4011                render: Arc::new(|_| div().into_any()),
 4012                priority: 0,
 4013            }],
 4014            Some(Autoscroll::fit()),
 4015            cx,
 4016        );
 4017        editor.change_selections(None, cx, |s| {
 4018            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4019        });
 4020        editor.move_line_down(&MoveLineDown, cx);
 4021    });
 4022}
 4023
 4024#[gpui::test]
 4025async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4026    init_test(cx, |_| {});
 4027
 4028    let mut cx = EditorTestContext::new(cx).await;
 4029    cx.set_state(
 4030        &"
 4031            ˇzero
 4032            one
 4033            two
 4034            three
 4035            four
 4036            five
 4037        "
 4038        .unindent(),
 4039    );
 4040
 4041    // Create a four-line block that replaces three lines of text.
 4042    cx.update_editor(|editor, cx| {
 4043        let snapshot = editor.snapshot(cx);
 4044        let snapshot = &snapshot.buffer_snapshot;
 4045        let placement = BlockPlacement::Replace(
 4046            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4047        );
 4048        editor.insert_blocks(
 4049            [BlockProperties {
 4050                placement,
 4051                height: 4,
 4052                style: BlockStyle::Sticky,
 4053                render: Arc::new(|_| gpui::div().into_any_element()),
 4054                priority: 0,
 4055            }],
 4056            None,
 4057            cx,
 4058        );
 4059    });
 4060
 4061    // Move down so that the cursor touches the block.
 4062    cx.update_editor(|editor, cx| {
 4063        editor.move_down(&Default::default(), cx);
 4064    });
 4065    cx.assert_editor_state(
 4066        &"
 4067            zero
 4068            «one
 4069            two
 4070            threeˇ»
 4071            four
 4072            five
 4073        "
 4074        .unindent(),
 4075    );
 4076
 4077    // Move down past the block.
 4078    cx.update_editor(|editor, cx| {
 4079        editor.move_down(&Default::default(), cx);
 4080    });
 4081    cx.assert_editor_state(
 4082        &"
 4083            zero
 4084            one
 4085            two
 4086            three
 4087            ˇfour
 4088            five
 4089        "
 4090        .unindent(),
 4091    );
 4092}
 4093
 4094#[gpui::test]
 4095fn test_transpose(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    _ = cx.add_window(|cx| {
 4099        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4100        editor.set_style(EditorStyle::default(), cx);
 4101        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "bac");
 4104        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4105
 4106        editor.transpose(&Default::default(), cx);
 4107        assert_eq!(editor.text(cx), "bca");
 4108        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4109
 4110        editor.transpose(&Default::default(), cx);
 4111        assert_eq!(editor.text(cx), "bac");
 4112        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4113
 4114        editor
 4115    });
 4116
 4117    _ = cx.add_window(|cx| {
 4118        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4119        editor.set_style(EditorStyle::default(), cx);
 4120        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "acb\nde");
 4123        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4124
 4125        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4126        editor.transpose(&Default::default(), cx);
 4127        assert_eq!(editor.text(cx), "acbd\ne");
 4128        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4129
 4130        editor.transpose(&Default::default(), cx);
 4131        assert_eq!(editor.text(cx), "acbde\n");
 4132        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4133
 4134        editor.transpose(&Default::default(), cx);
 4135        assert_eq!(editor.text(cx), "acbd\ne");
 4136        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4137
 4138        editor
 4139    });
 4140
 4141    _ = cx.add_window(|cx| {
 4142        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4143        editor.set_style(EditorStyle::default(), cx);
 4144        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4145        editor.transpose(&Default::default(), cx);
 4146        assert_eq!(editor.text(cx), "bacd\ne");
 4147        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4148
 4149        editor.transpose(&Default::default(), cx);
 4150        assert_eq!(editor.text(cx), "bcade\n");
 4151        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4152
 4153        editor.transpose(&Default::default(), cx);
 4154        assert_eq!(editor.text(cx), "bcda\ne");
 4155        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4156
 4157        editor.transpose(&Default::default(), cx);
 4158        assert_eq!(editor.text(cx), "bcade\n");
 4159        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4160
 4161        editor.transpose(&Default::default(), cx);
 4162        assert_eq!(editor.text(cx), "bcaed\n");
 4163        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4164
 4165        editor
 4166    });
 4167
 4168    _ = cx.add_window(|cx| {
 4169        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4170        editor.set_style(EditorStyle::default(), cx);
 4171        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4172        editor.transpose(&Default::default(), cx);
 4173        assert_eq!(editor.text(cx), "🏀🍐✋");
 4174        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4175
 4176        editor.transpose(&Default::default(), cx);
 4177        assert_eq!(editor.text(cx), "🏀✋🍐");
 4178        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4179
 4180        editor.transpose(&Default::default(), cx);
 4181        assert_eq!(editor.text(cx), "🏀🍐✋");
 4182        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4183
 4184        editor
 4185    });
 4186}
 4187
 4188#[gpui::test]
 4189async fn test_rewrap(cx: &mut TestAppContext) {
 4190    init_test(cx, |_| {});
 4191
 4192    let mut cx = EditorTestContext::new(cx).await;
 4193
 4194    let language_with_c_comments = Arc::new(Language::new(
 4195        LanguageConfig {
 4196            line_comments: vec!["// ".into()],
 4197            ..LanguageConfig::default()
 4198        },
 4199        None,
 4200    ));
 4201    let language_with_pound_comments = Arc::new(Language::new(
 4202        LanguageConfig {
 4203            line_comments: vec!["# ".into()],
 4204            ..LanguageConfig::default()
 4205        },
 4206        None,
 4207    ));
 4208    let markdown_language = Arc::new(Language::new(
 4209        LanguageConfig {
 4210            name: "Markdown".into(),
 4211            ..LanguageConfig::default()
 4212        },
 4213        None,
 4214    ));
 4215    let language_with_doc_comments = Arc::new(Language::new(
 4216        LanguageConfig {
 4217            line_comments: vec!["// ".into(), "/// ".into()],
 4218            ..LanguageConfig::default()
 4219        },
 4220        Some(tree_sitter_rust::LANGUAGE.into()),
 4221    ));
 4222
 4223    let plaintext_language = Arc::new(Language::new(
 4224        LanguageConfig {
 4225            name: "Plain Text".into(),
 4226            ..LanguageConfig::default()
 4227        },
 4228        None,
 4229    ));
 4230
 4231    assert_rewrap(
 4232        indoc! {"
 4233            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4234        "},
 4235        indoc! {"
 4236            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4237            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4238            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4239            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4240            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4241            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4242            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4243            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4244            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4245            // porttitor id. Aliquam id accumsan eros.
 4246        "},
 4247        language_with_c_comments.clone(),
 4248        &mut cx,
 4249    );
 4250
 4251    // Test that rewrapping works inside of a selection
 4252    assert_rewrap(
 4253        indoc! {"
 4254            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4255        "},
 4256        indoc! {"
 4257            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4258            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4259            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4260            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4261            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4262            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4263            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4264            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4265            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4266            // porttitor id. Aliquam id accumsan eros.ˇ»
 4267        "},
 4268        language_with_c_comments.clone(),
 4269        &mut cx,
 4270    );
 4271
 4272    // Test that cursors that expand to the same region are collapsed.
 4273    assert_rewrap(
 4274        indoc! {"
 4275            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4276            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4277            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4278            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4279        "},
 4280        indoc! {"
 4281            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4282            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4283            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4284            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4285            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4286            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4287            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4288            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4289            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4290            // porttitor id. Aliquam id accumsan eros.
 4291        "},
 4292        language_with_c_comments.clone(),
 4293        &mut cx,
 4294    );
 4295
 4296    // Test that non-contiguous selections are treated separately.
 4297    assert_rewrap(
 4298        indoc! {"
 4299            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4300            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4301            //
 4302            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4303            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4304        "},
 4305        indoc! {"
 4306            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4307            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4308            // auctor, eu lacinia sapien scelerisque.
 4309            //
 4310            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4311            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4312            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4313            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4314            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4315            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4316            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4317        "},
 4318        language_with_c_comments.clone(),
 4319        &mut cx,
 4320    );
 4321
 4322    // Test that different comment prefixes are supported.
 4323    assert_rewrap(
 4324        indoc! {"
 4325            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4326        "},
 4327        indoc! {"
 4328            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4329            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4330            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4331            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4332            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4333            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4334            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4335            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4336            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4337            # accumsan eros.
 4338        "},
 4339        language_with_pound_comments.clone(),
 4340        &mut cx,
 4341    );
 4342
 4343    // Test that rewrapping is ignored outside of comments in most languages.
 4344    assert_rewrap(
 4345        indoc! {"
 4346            /// Adds two numbers.
 4347            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4348            fn add(a: u32, b: u32) -> u32 {
 4349                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4350            }
 4351        "},
 4352        indoc! {"
 4353            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4354            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4355            fn add(a: u32, b: u32) -> u32 {
 4356                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4357            }
 4358        "},
 4359        language_with_doc_comments.clone(),
 4360        &mut cx,
 4361    );
 4362
 4363    // Test that rewrapping works in Markdown and Plain Text languages.
 4364    assert_rewrap(
 4365        indoc! {"
 4366            # Hello
 4367
 4368            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4369        "},
 4370        indoc! {"
 4371            # Hello
 4372
 4373            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4374            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4375            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4376            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4377            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4378            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4379            Integer sit amet scelerisque nisi.
 4380        "},
 4381        markdown_language,
 4382        &mut cx,
 4383    );
 4384
 4385    assert_rewrap(
 4386        indoc! {"
 4387            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4388        "},
 4389        indoc! {"
 4390            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4391            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4392            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4393            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4394            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4395            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4396            Integer sit amet scelerisque nisi.
 4397        "},
 4398        plaintext_language,
 4399        &mut cx,
 4400    );
 4401
 4402    // Test rewrapping unaligned comments in a selection.
 4403    assert_rewrap(
 4404        indoc! {"
 4405            fn foo() {
 4406                if true {
 4407            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4408            // Praesent semper egestas tellus id dignissim.ˇ»
 4409                    do_something();
 4410                } else {
 4411                    //
 4412                }
 4413            }
 4414        "},
 4415        indoc! {"
 4416            fn foo() {
 4417                if true {
 4418            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4419                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4420                    // egestas tellus id dignissim.ˇ»
 4421                    do_something();
 4422                } else {
 4423                    //
 4424                }
 4425            }
 4426        "},
 4427        language_with_doc_comments.clone(),
 4428        &mut cx,
 4429    );
 4430
 4431    assert_rewrap(
 4432        indoc! {"
 4433            fn foo() {
 4434                if true {
 4435            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4436            // Praesent semper egestas tellus id dignissim.»
 4437                    do_something();
 4438                } else {
 4439                    //
 4440                }
 4441
 4442            }
 4443        "},
 4444        indoc! {"
 4445            fn foo() {
 4446                if true {
 4447            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4448                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4449                    // egestas tellus id dignissim.»
 4450                    do_something();
 4451                } else {
 4452                    //
 4453                }
 4454
 4455            }
 4456        "},
 4457        language_with_doc_comments.clone(),
 4458        &mut cx,
 4459    );
 4460
 4461    #[track_caller]
 4462    fn assert_rewrap(
 4463        unwrapped_text: &str,
 4464        wrapped_text: &str,
 4465        language: Arc<Language>,
 4466        cx: &mut EditorTestContext,
 4467    ) {
 4468        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4469        cx.set_state(unwrapped_text);
 4470        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4471        cx.assert_editor_state(wrapped_text);
 4472    }
 4473}
 4474
 4475#[gpui::test]
 4476async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4477    init_test(cx, |_| {});
 4478
 4479    let mut cx = EditorTestContext::new(cx).await;
 4480
 4481    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4482    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4483    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4484
 4485    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4486    cx.set_state("two ˇfour ˇsix ˇ");
 4487    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4488    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4489
 4490    // Paste again but with only two cursors. Since the number of cursors doesn't
 4491    // match the number of slices in the clipboard, the entire clipboard text
 4492    // is pasted at each cursor.
 4493    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4494    cx.update_editor(|e, cx| {
 4495        e.handle_input("( ", cx);
 4496        e.paste(&Paste, cx);
 4497        e.handle_input(") ", cx);
 4498    });
 4499    cx.assert_editor_state(
 4500        &([
 4501            "( one✅ ",
 4502            "three ",
 4503            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4504            "three ",
 4505            "five ) ˇ",
 4506        ]
 4507        .join("\n")),
 4508    );
 4509
 4510    // Cut with three selections, one of which is full-line.
 4511    cx.set_state(indoc! {"
 4512        1«2ˇ»3
 4513        4ˇ567
 4514        «8ˇ»9"});
 4515    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4516    cx.assert_editor_state(indoc! {"
 4517        1ˇ3
 4518        ˇ9"});
 4519
 4520    // Paste with three selections, noticing how the copied selection that was full-line
 4521    // gets inserted before the second cursor.
 4522    cx.set_state(indoc! {"
 4523        1ˇ3
 4524 4525        «oˇ»ne"});
 4526    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4527    cx.assert_editor_state(indoc! {"
 4528        12ˇ3
 4529        4567
 4530 4531        8ˇne"});
 4532
 4533    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4534    cx.set_state(indoc! {"
 4535        The quick brown
 4536        fox juˇmps over
 4537        the lazy dog"});
 4538    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4539    assert_eq!(
 4540        cx.read_from_clipboard()
 4541            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4542        Some("fox jumps over\n".to_string())
 4543    );
 4544
 4545    // Paste with three selections, noticing how the copied full-line selection is inserted
 4546    // before the empty selections but replaces the selection that is non-empty.
 4547    cx.set_state(indoc! {"
 4548        Tˇhe quick brown
 4549        «foˇ»x jumps over
 4550        tˇhe lazy dog"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        fox jumps over
 4554        Tˇhe quick brown
 4555        fox jumps over
 4556        ˇx jumps over
 4557        fox jumps over
 4558        tˇhe lazy dog"});
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new(cx).await;
 4566    let language = Arc::new(Language::new(
 4567        LanguageConfig::default(),
 4568        Some(tree_sitter_rust::LANGUAGE.into()),
 4569    ));
 4570    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4571
 4572    // Cut an indented block, without the leading whitespace.
 4573    cx.set_state(indoc! {"
 4574        const a: B = (
 4575            c(),
 4576            «d(
 4577                e,
 4578                f
 4579            )ˇ»
 4580        );
 4581    "});
 4582    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4583    cx.assert_editor_state(indoc! {"
 4584        const a: B = (
 4585            c(),
 4586            ˇ
 4587        );
 4588    "});
 4589
 4590    // Paste it at the same position.
 4591    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4592    cx.assert_editor_state(indoc! {"
 4593        const a: B = (
 4594            c(),
 4595            d(
 4596                e,
 4597                f
 4598 4599        );
 4600    "});
 4601
 4602    // Paste it at a line with a lower indent level.
 4603    cx.set_state(indoc! {"
 4604        ˇ
 4605        const a: B = (
 4606            c(),
 4607        );
 4608    "});
 4609    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4610    cx.assert_editor_state(indoc! {"
 4611        d(
 4612            e,
 4613            f
 4614 4615        const a: B = (
 4616            c(),
 4617        );
 4618    "});
 4619
 4620    // Cut an indented block, with the leading whitespace.
 4621    cx.set_state(indoc! {"
 4622        const a: B = (
 4623            c(),
 4624        «    d(
 4625                e,
 4626                f
 4627            )
 4628        ˇ»);
 4629    "});
 4630    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4631    cx.assert_editor_state(indoc! {"
 4632        const a: B = (
 4633            c(),
 4634        ˇ);
 4635    "});
 4636
 4637    // Paste it at the same position.
 4638    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4639    cx.assert_editor_state(indoc! {"
 4640        const a: B = (
 4641            c(),
 4642            d(
 4643                e,
 4644                f
 4645            )
 4646        ˇ);
 4647    "});
 4648
 4649    // Paste it at a line with a higher indent level.
 4650    cx.set_state(indoc! {"
 4651        const a: B = (
 4652            c(),
 4653            d(
 4654                e,
 4655 4656            )
 4657        );
 4658    "});
 4659    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4660    cx.assert_editor_state(indoc! {"
 4661        const a: B = (
 4662            c(),
 4663            d(
 4664                e,
 4665                f    d(
 4666                    e,
 4667                    f
 4668                )
 4669        ˇ
 4670            )
 4671        );
 4672    "});
 4673}
 4674
 4675#[gpui::test]
 4676fn test_select_all(cx: &mut TestAppContext) {
 4677    init_test(cx, |_| {});
 4678
 4679    let view = cx.add_window(|cx| {
 4680        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4681        build_editor(buffer, cx)
 4682    });
 4683    _ = view.update(cx, |view, cx| {
 4684        view.select_all(&SelectAll, cx);
 4685        assert_eq!(
 4686            view.selections.display_ranges(cx),
 4687            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4688        );
 4689    });
 4690}
 4691
 4692#[gpui::test]
 4693fn test_select_line(cx: &mut TestAppContext) {
 4694    init_test(cx, |_| {});
 4695
 4696    let view = cx.add_window(|cx| {
 4697        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4698        build_editor(buffer, cx)
 4699    });
 4700    _ = view.update(cx, |view, cx| {
 4701        view.change_selections(None, cx, |s| {
 4702            s.select_display_ranges([
 4703                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4704                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4705                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4706                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4707            ])
 4708        });
 4709        view.select_line(&SelectLine, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            vec![
 4713                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4715            ]
 4716        );
 4717    });
 4718
 4719    _ = view.update(cx, |view, cx| {
 4720        view.select_line(&SelectLine, cx);
 4721        assert_eq!(
 4722            view.selections.display_ranges(cx),
 4723            vec![
 4724                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4725                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4726            ]
 4727        );
 4728    });
 4729
 4730    _ = view.update(cx, |view, cx| {
 4731        view.select_line(&SelectLine, cx);
 4732        assert_eq!(
 4733            view.selections.display_ranges(cx),
 4734            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4735        );
 4736    });
 4737}
 4738
 4739#[gpui::test]
 4740fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4741    init_test(cx, |_| {});
 4742
 4743    let view = cx.add_window(|cx| {
 4744        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4745        build_editor(buffer, cx)
 4746    });
 4747    _ = view.update(cx, |view, cx| {
 4748        view.fold_creases(
 4749            vec![
 4750                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4751                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4752                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4753            ],
 4754            true,
 4755            cx,
 4756        );
 4757        view.change_selections(None, cx, |s| {
 4758            s.select_display_ranges([
 4759                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4760                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4761                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4762                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4763            ])
 4764        });
 4765        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4766    });
 4767
 4768    _ = view.update(cx, |view, cx| {
 4769        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4770        assert_eq!(
 4771            view.display_text(cx),
 4772            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4773        );
 4774        assert_eq!(
 4775            view.selections.display_ranges(cx),
 4776            [
 4777                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4778                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4779                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4780                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4781            ]
 4782        );
 4783    });
 4784
 4785    _ = view.update(cx, |view, cx| {
 4786        view.change_selections(None, cx, |s| {
 4787            s.select_display_ranges([
 4788                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4789            ])
 4790        });
 4791        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4792        assert_eq!(
 4793            view.display_text(cx),
 4794            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4795        );
 4796        assert_eq!(
 4797            view.selections.display_ranges(cx),
 4798            [
 4799                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4800                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4801                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4802                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4803                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4804                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4805                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4806                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4807            ]
 4808        );
 4809    });
 4810}
 4811
 4812#[gpui::test]
 4813async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4814    init_test(cx, |_| {});
 4815
 4816    let mut cx = EditorTestContext::new(cx).await;
 4817
 4818    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4819    cx.set_state(indoc!(
 4820        r#"abc
 4821           defˇghi
 4822
 4823           jk
 4824           nlmo
 4825           "#
 4826    ));
 4827
 4828    cx.update_editor(|editor, cx| {
 4829        editor.add_selection_above(&Default::default(), cx);
 4830    });
 4831
 4832    cx.assert_editor_state(indoc!(
 4833        r#"abcˇ
 4834           defˇghi
 4835
 4836           jk
 4837           nlmo
 4838           "#
 4839    ));
 4840
 4841    cx.update_editor(|editor, cx| {
 4842        editor.add_selection_above(&Default::default(), cx);
 4843    });
 4844
 4845    cx.assert_editor_state(indoc!(
 4846        r#"abcˇ
 4847            defˇghi
 4848
 4849            jk
 4850            nlmo
 4851            "#
 4852    ));
 4853
 4854    cx.update_editor(|view, cx| {
 4855        view.add_selection_below(&Default::default(), cx);
 4856    });
 4857
 4858    cx.assert_editor_state(indoc!(
 4859        r#"abc
 4860           defˇghi
 4861
 4862           jk
 4863           nlmo
 4864           "#
 4865    ));
 4866
 4867    cx.update_editor(|view, cx| {
 4868        view.undo_selection(&Default::default(), cx);
 4869    });
 4870
 4871    cx.assert_editor_state(indoc!(
 4872        r#"abcˇ
 4873           defˇghi
 4874
 4875           jk
 4876           nlmo
 4877           "#
 4878    ));
 4879
 4880    cx.update_editor(|view, cx| {
 4881        view.redo_selection(&Default::default(), cx);
 4882    });
 4883
 4884    cx.assert_editor_state(indoc!(
 4885        r#"abc
 4886           defˇghi
 4887
 4888           jk
 4889           nlmo
 4890           "#
 4891    ));
 4892
 4893    cx.update_editor(|view, cx| {
 4894        view.add_selection_below(&Default::default(), cx);
 4895    });
 4896
 4897    cx.assert_editor_state(indoc!(
 4898        r#"abc
 4899           defˇghi
 4900
 4901           jk
 4902           nlmˇo
 4903           "#
 4904    ));
 4905
 4906    cx.update_editor(|view, cx| {
 4907        view.add_selection_below(&Default::default(), cx);
 4908    });
 4909
 4910    cx.assert_editor_state(indoc!(
 4911        r#"abc
 4912           defˇghi
 4913
 4914           jk
 4915           nlmˇo
 4916           "#
 4917    ));
 4918
 4919    // change selections
 4920    cx.set_state(indoc!(
 4921        r#"abc
 4922           def«ˇg»hi
 4923
 4924           jk
 4925           nlmo
 4926           "#
 4927    ));
 4928
 4929    cx.update_editor(|view, cx| {
 4930        view.add_selection_below(&Default::default(), cx);
 4931    });
 4932
 4933    cx.assert_editor_state(indoc!(
 4934        r#"abc
 4935           def«ˇg»hi
 4936
 4937           jk
 4938           nlm«ˇo»
 4939           "#
 4940    ));
 4941
 4942    cx.update_editor(|view, cx| {
 4943        view.add_selection_below(&Default::default(), cx);
 4944    });
 4945
 4946    cx.assert_editor_state(indoc!(
 4947        r#"abc
 4948           def«ˇg»hi
 4949
 4950           jk
 4951           nlm«ˇo»
 4952           "#
 4953    ));
 4954
 4955    cx.update_editor(|view, cx| {
 4956        view.add_selection_above(&Default::default(), cx);
 4957    });
 4958
 4959    cx.assert_editor_state(indoc!(
 4960        r#"abc
 4961           def«ˇg»hi
 4962
 4963           jk
 4964           nlmo
 4965           "#
 4966    ));
 4967
 4968    cx.update_editor(|view, cx| {
 4969        view.add_selection_above(&Default::default(), cx);
 4970    });
 4971
 4972    cx.assert_editor_state(indoc!(
 4973        r#"abc
 4974           def«ˇg»hi
 4975
 4976           jk
 4977           nlmo
 4978           "#
 4979    ));
 4980
 4981    // Change selections again
 4982    cx.set_state(indoc!(
 4983        r#"a«bc
 4984           defgˇ»hi
 4985
 4986           jk
 4987           nlmo
 4988           "#
 4989    ));
 4990
 4991    cx.update_editor(|view, cx| {
 4992        view.add_selection_below(&Default::default(), cx);
 4993    });
 4994
 4995    cx.assert_editor_state(indoc!(
 4996        r#"a«bcˇ»
 4997           d«efgˇ»hi
 4998
 4999           j«kˇ»
 5000           nlmo
 5001           "#
 5002    ));
 5003
 5004    cx.update_editor(|view, cx| {
 5005        view.add_selection_below(&Default::default(), cx);
 5006    });
 5007    cx.assert_editor_state(indoc!(
 5008        r#"a«bcˇ»
 5009           d«efgˇ»hi
 5010
 5011           j«kˇ»
 5012           n«lmoˇ»
 5013           "#
 5014    ));
 5015    cx.update_editor(|view, cx| {
 5016        view.add_selection_above(&Default::default(), cx);
 5017    });
 5018
 5019    cx.assert_editor_state(indoc!(
 5020        r#"a«bcˇ»
 5021           d«efgˇ»hi
 5022
 5023           j«kˇ»
 5024           nlmo
 5025           "#
 5026    ));
 5027
 5028    // Change selections again
 5029    cx.set_state(indoc!(
 5030        r#"abc
 5031           d«ˇefghi
 5032
 5033           jk
 5034           nlm»o
 5035           "#
 5036    ));
 5037
 5038    cx.update_editor(|view, cx| {
 5039        view.add_selection_above(&Default::default(), cx);
 5040    });
 5041
 5042    cx.assert_editor_state(indoc!(
 5043        r#"a«ˇbc»
 5044           d«ˇef»ghi
 5045
 5046           j«ˇk»
 5047           n«ˇlm»o
 5048           "#
 5049    ));
 5050
 5051    cx.update_editor(|view, cx| {
 5052        view.add_selection_below(&Default::default(), cx);
 5053    });
 5054
 5055    cx.assert_editor_state(indoc!(
 5056        r#"abc
 5057           d«ˇef»ghi
 5058
 5059           j«ˇk»
 5060           n«ˇlm»o
 5061           "#
 5062    ));
 5063}
 5064
 5065#[gpui::test]
 5066async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5067    init_test(cx, |_| {});
 5068
 5069    let mut cx = EditorTestContext::new(cx).await;
 5070    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5071
 5072    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5073        .unwrap();
 5074    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5075
 5076    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5077        .unwrap();
 5078    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5079
 5080    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5081    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5082
 5083    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5084    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5085
 5086    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5087        .unwrap();
 5088    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5089
 5090    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5091        .unwrap();
 5092    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5093}
 5094
 5095#[gpui::test]
 5096async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5097    init_test(cx, |_| {});
 5098
 5099    let mut cx = EditorTestContext::new(cx).await;
 5100    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5101
 5102    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5103        .unwrap();
 5104    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5105}
 5106
 5107#[gpui::test]
 5108async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5109    init_test(cx, |_| {});
 5110
 5111    let mut cx = EditorTestContext::new(cx).await;
 5112    cx.set_state(
 5113        r#"let foo = 2;
 5114lˇet foo = 2;
 5115let fooˇ = 2;
 5116let foo = 2;
 5117let foo = ˇ2;"#,
 5118    );
 5119
 5120    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5121        .unwrap();
 5122    cx.assert_editor_state(
 5123        r#"let foo = 2;
 5124«letˇ» foo = 2;
 5125let «fooˇ» = 2;
 5126let foo = 2;
 5127let foo = «2ˇ»;"#,
 5128    );
 5129
 5130    // noop for multiple selections with different contents
 5131    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5132        .unwrap();
 5133    cx.assert_editor_state(
 5134        r#"let foo = 2;
 5135«letˇ» foo = 2;
 5136let «fooˇ» = 2;
 5137let foo = 2;
 5138let foo = «2ˇ»;"#,
 5139    );
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |_| {});
 5145
 5146    let mut cx =
 5147        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5148
 5149    cx.assert_editor_state(indoc! {"
 5150        ˇbbb
 5151        ccc
 5152
 5153        bbb
 5154        ccc
 5155        "});
 5156    cx.dispatch_action(SelectPrevious::default());
 5157    cx.assert_editor_state(indoc! {"
 5158                «bbbˇ»
 5159                ccc
 5160
 5161                bbb
 5162                ccc
 5163                "});
 5164    cx.dispatch_action(SelectPrevious::default());
 5165    cx.assert_editor_state(indoc! {"
 5166                «bbbˇ»
 5167                ccc
 5168
 5169                «bbbˇ»
 5170                ccc
 5171                "});
 5172}
 5173
 5174#[gpui::test]
 5175async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5176    init_test(cx, |_| {});
 5177
 5178    let mut cx = EditorTestContext::new(cx).await;
 5179    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5180
 5181    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5182        .unwrap();
 5183    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5184
 5185    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5186        .unwrap();
 5187    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5188
 5189    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5190    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5191
 5192    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5193    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5194
 5195    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5196        .unwrap();
 5197    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5198
 5199    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5200        .unwrap();
 5201    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5202
 5203    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5204        .unwrap();
 5205    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5206}
 5207
 5208#[gpui::test]
 5209async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5210    init_test(cx, |_| {});
 5211
 5212    let mut cx = EditorTestContext::new(cx).await;
 5213    cx.set_state(
 5214        r#"let foo = 2;
 5215lˇet foo = 2;
 5216let fooˇ = 2;
 5217let foo = 2;
 5218let foo = ˇ2;"#,
 5219    );
 5220
 5221    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5222        .unwrap();
 5223    cx.assert_editor_state(
 5224        r#"let foo = 2;
 5225«letˇ» foo = 2;
 5226let «fooˇ» = 2;
 5227let foo = 2;
 5228let foo = «2ˇ»;"#,
 5229    );
 5230
 5231    // noop for multiple selections with different contents
 5232    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5233        .unwrap();
 5234    cx.assert_editor_state(
 5235        r#"let foo = 2;
 5236«letˇ» foo = 2;
 5237let «fooˇ» = 2;
 5238let foo = 2;
 5239let foo = «2ˇ»;"#,
 5240    );
 5241}
 5242
 5243#[gpui::test]
 5244async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5245    init_test(cx, |_| {});
 5246
 5247    let mut cx = EditorTestContext::new(cx).await;
 5248    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5249
 5250    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5251        .unwrap();
 5252    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5253
 5254    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5255        .unwrap();
 5256    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5257
 5258    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5259    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5260
 5261    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5262    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5263
 5264    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5265        .unwrap();
 5266    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5267
 5268    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5269        .unwrap();
 5270    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5271}
 5272
 5273#[gpui::test]
 5274async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5275    init_test(cx, |_| {});
 5276
 5277    let language = Arc::new(Language::new(
 5278        LanguageConfig::default(),
 5279        Some(tree_sitter_rust::LANGUAGE.into()),
 5280    ));
 5281
 5282    let text = r#"
 5283        use mod1::mod2::{mod3, mod4};
 5284
 5285        fn fn_1(param1: bool, param2: &str) {
 5286            let var1 = "text";
 5287        }
 5288    "#
 5289    .unindent();
 5290
 5291    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5293    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5294
 5295    editor
 5296        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5297        .await;
 5298
 5299    editor.update(cx, |view, cx| {
 5300        view.change_selections(None, cx, |s| {
 5301            s.select_display_ranges([
 5302                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5303                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5304                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5305            ]);
 5306        });
 5307        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5308    });
 5309    editor.update(cx, |editor, cx| {
 5310        assert_text_with_selections(
 5311            editor,
 5312            indoc! {r#"
 5313                use mod1::mod2::{mod3, «mod4ˇ»};
 5314
 5315                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5316                    let var1 = "«textˇ»";
 5317                }
 5318            "#},
 5319            cx,
 5320        );
 5321    });
 5322
 5323    editor.update(cx, |view, cx| {
 5324        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5325    });
 5326    editor.update(cx, |editor, cx| {
 5327        assert_text_with_selections(
 5328            editor,
 5329            indoc! {r#"
 5330                use mod1::mod2::«{mod3, mod4}ˇ»;
 5331
 5332                «ˇfn fn_1(param1: bool, param2: &str) {
 5333                    let var1 = "text";
 5334 5335            "#},
 5336            cx,
 5337        );
 5338    });
 5339
 5340    editor.update(cx, |view, cx| {
 5341        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5342    });
 5343    assert_eq!(
 5344        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5345        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5346    );
 5347
 5348    // Trying to expand the selected syntax node one more time has no effect.
 5349    editor.update(cx, |view, cx| {
 5350        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5351    });
 5352    assert_eq!(
 5353        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5354        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5355    );
 5356
 5357    editor.update(cx, |view, cx| {
 5358        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5359    });
 5360    editor.update(cx, |editor, cx| {
 5361        assert_text_with_selections(
 5362            editor,
 5363            indoc! {r#"
 5364                use mod1::mod2::«{mod3, mod4}ˇ»;
 5365
 5366                «ˇfn fn_1(param1: bool, param2: &str) {
 5367                    let var1 = "text";
 5368 5369            "#},
 5370            cx,
 5371        );
 5372    });
 5373
 5374    editor.update(cx, |view, cx| {
 5375        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5376    });
 5377    editor.update(cx, |editor, cx| {
 5378        assert_text_with_selections(
 5379            editor,
 5380            indoc! {r#"
 5381                use mod1::mod2::{mod3, «mod4ˇ»};
 5382
 5383                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5384                    let var1 = "«textˇ»";
 5385                }
 5386            "#},
 5387            cx,
 5388        );
 5389    });
 5390
 5391    editor.update(cx, |view, cx| {
 5392        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5393    });
 5394    editor.update(cx, |editor, cx| {
 5395        assert_text_with_selections(
 5396            editor,
 5397            indoc! {r#"
 5398                use mod1::mod2::{mod3, mo«ˇ»d4};
 5399
 5400                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5401                    let var1 = "te«ˇ»xt";
 5402                }
 5403            "#},
 5404            cx,
 5405        );
 5406    });
 5407
 5408    // Trying to shrink the selected syntax node one more time has no effect.
 5409    editor.update(cx, |view, cx| {
 5410        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5411    });
 5412    editor.update(cx, |editor, cx| {
 5413        assert_text_with_selections(
 5414            editor,
 5415            indoc! {r#"
 5416                use mod1::mod2::{mod3, mo«ˇ»d4};
 5417
 5418                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5419                    let var1 = "te«ˇ»xt";
 5420                }
 5421            "#},
 5422            cx,
 5423        );
 5424    });
 5425
 5426    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5427    // a fold.
 5428    editor.update(cx, |view, cx| {
 5429        view.fold_creases(
 5430            vec![
 5431                Crease::simple(
 5432                    Point::new(0, 21)..Point::new(0, 24),
 5433                    FoldPlaceholder::test(),
 5434                ),
 5435                Crease::simple(
 5436                    Point::new(3, 20)..Point::new(3, 22),
 5437                    FoldPlaceholder::test(),
 5438                ),
 5439            ],
 5440            true,
 5441            cx,
 5442        );
 5443        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5444    });
 5445    editor.update(cx, |editor, cx| {
 5446        assert_text_with_selections(
 5447            editor,
 5448            indoc! {r#"
 5449                use mod1::mod2::«{mod3, mod4}ˇ»;
 5450
 5451                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5452                    «let var1 = "text";ˇ»
 5453                }
 5454            "#},
 5455            cx,
 5456        );
 5457    });
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let language = Arc::new(
 5465        Language::new(
 5466            LanguageConfig {
 5467                brackets: BracketPairConfig {
 5468                    pairs: vec![
 5469                        BracketPair {
 5470                            start: "{".to_string(),
 5471                            end: "}".to_string(),
 5472                            close: false,
 5473                            surround: false,
 5474                            newline: true,
 5475                        },
 5476                        BracketPair {
 5477                            start: "(".to_string(),
 5478                            end: ")".to_string(),
 5479                            close: false,
 5480                            surround: false,
 5481                            newline: true,
 5482                        },
 5483                    ],
 5484                    ..Default::default()
 5485                },
 5486                ..Default::default()
 5487            },
 5488            Some(tree_sitter_rust::LANGUAGE.into()),
 5489        )
 5490        .with_indents_query(
 5491            r#"
 5492                (_ "(" ")" @end) @indent
 5493                (_ "{" "}" @end) @indent
 5494            "#,
 5495        )
 5496        .unwrap(),
 5497    );
 5498
 5499    let text = "fn a() {}";
 5500
 5501    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5502    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5503    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5504    editor
 5505        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5506        .await;
 5507
 5508    editor.update(cx, |editor, cx| {
 5509        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5510        editor.newline(&Newline, cx);
 5511        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5512        assert_eq!(
 5513            editor.selections.ranges(cx),
 5514            &[
 5515                Point::new(1, 4)..Point::new(1, 4),
 5516                Point::new(3, 4)..Point::new(3, 4),
 5517                Point::new(5, 0)..Point::new(5, 0)
 5518            ]
 5519        );
 5520    });
 5521}
 5522
 5523#[gpui::test]
 5524async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5525    init_test(cx, |_| {});
 5526
 5527    {
 5528        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5529        cx.set_state(indoc! {"
 5530            impl A {
 5531
 5532                fn b() {}
 5533
 5534            «fn c() {
 5535
 5536            }ˇ»
 5537            }
 5538        "});
 5539
 5540        cx.update_editor(|editor, cx| {
 5541            editor.autoindent(&Default::default(), cx);
 5542        });
 5543
 5544        cx.assert_editor_state(indoc! {"
 5545            impl A {
 5546
 5547                fn b() {}
 5548
 5549                «fn c() {
 5550
 5551                }ˇ»
 5552            }
 5553        "});
 5554    }
 5555
 5556    {
 5557        let mut cx = EditorTestContext::new_multibuffer(
 5558            cx,
 5559            [indoc! { "
 5560                impl A {
 5561                «
 5562                // a
 5563                fn b(){}
 5564                »
 5565                «
 5566                    }
 5567                    fn c(){}
 5568                »
 5569            "}],
 5570        );
 5571
 5572        let buffer = cx.update_editor(|editor, cx| {
 5573            let buffer = editor.buffer().update(cx, |buffer, _| {
 5574                buffer.all_buffers().iter().next().unwrap().clone()
 5575            });
 5576            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5577            buffer
 5578        });
 5579
 5580        cx.run_until_parked();
 5581        cx.update_editor(|editor, cx| {
 5582            editor.select_all(&Default::default(), cx);
 5583            editor.autoindent(&Default::default(), cx)
 5584        });
 5585        cx.run_until_parked();
 5586
 5587        cx.update(|cx| {
 5588            pretty_assertions::assert_eq!(
 5589                buffer.read(cx).text(),
 5590                indoc! { "
 5591                    impl A {
 5592
 5593                        // a
 5594                        fn b(){}
 5595
 5596
 5597                    }
 5598                    fn c(){}
 5599
 5600                " }
 5601            )
 5602        });
 5603    }
 5604}
 5605
 5606#[gpui::test]
 5607async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5608    init_test(cx, |_| {});
 5609
 5610    let mut cx = EditorTestContext::new(cx).await;
 5611
 5612    let language = Arc::new(Language::new(
 5613        LanguageConfig {
 5614            brackets: BracketPairConfig {
 5615                pairs: vec![
 5616                    BracketPair {
 5617                        start: "{".to_string(),
 5618                        end: "}".to_string(),
 5619                        close: true,
 5620                        surround: true,
 5621                        newline: true,
 5622                    },
 5623                    BracketPair {
 5624                        start: "(".to_string(),
 5625                        end: ")".to_string(),
 5626                        close: true,
 5627                        surround: true,
 5628                        newline: true,
 5629                    },
 5630                    BracketPair {
 5631                        start: "/*".to_string(),
 5632                        end: " */".to_string(),
 5633                        close: true,
 5634                        surround: true,
 5635                        newline: true,
 5636                    },
 5637                    BracketPair {
 5638                        start: "[".to_string(),
 5639                        end: "]".to_string(),
 5640                        close: false,
 5641                        surround: false,
 5642                        newline: true,
 5643                    },
 5644                    BracketPair {
 5645                        start: "\"".to_string(),
 5646                        end: "\"".to_string(),
 5647                        close: true,
 5648                        surround: true,
 5649                        newline: false,
 5650                    },
 5651                    BracketPair {
 5652                        start: "<".to_string(),
 5653                        end: ">".to_string(),
 5654                        close: false,
 5655                        surround: true,
 5656                        newline: true,
 5657                    },
 5658                ],
 5659                ..Default::default()
 5660            },
 5661            autoclose_before: "})]".to_string(),
 5662            ..Default::default()
 5663        },
 5664        Some(tree_sitter_rust::LANGUAGE.into()),
 5665    ));
 5666
 5667    cx.language_registry().add(language.clone());
 5668    cx.update_buffer(|buffer, cx| {
 5669        buffer.set_language(Some(language), cx);
 5670    });
 5671
 5672    cx.set_state(
 5673        &r#"
 5674            🏀ˇ
 5675            εˇ
 5676            ❤️ˇ
 5677        "#
 5678        .unindent(),
 5679    );
 5680
 5681    // autoclose multiple nested brackets at multiple cursors
 5682    cx.update_editor(|view, cx| {
 5683        view.handle_input("{", cx);
 5684        view.handle_input("{", cx);
 5685        view.handle_input("{", cx);
 5686    });
 5687    cx.assert_editor_state(
 5688        &"
 5689            🏀{{{ˇ}}}
 5690            ε{{{ˇ}}}
 5691            ❤️{{{ˇ}}}
 5692        "
 5693        .unindent(),
 5694    );
 5695
 5696    // insert a different closing bracket
 5697    cx.update_editor(|view, cx| {
 5698        view.handle_input(")", cx);
 5699    });
 5700    cx.assert_editor_state(
 5701        &"
 5702            🏀{{{)ˇ}}}
 5703            ε{{{)ˇ}}}
 5704            ❤️{{{)ˇ}}}
 5705        "
 5706        .unindent(),
 5707    );
 5708
 5709    // skip over the auto-closed brackets when typing a closing bracket
 5710    cx.update_editor(|view, cx| {
 5711        view.move_right(&MoveRight, cx);
 5712        view.handle_input("}", cx);
 5713        view.handle_input("}", cx);
 5714        view.handle_input("}", cx);
 5715    });
 5716    cx.assert_editor_state(
 5717        &"
 5718            🏀{{{)}}}}ˇ
 5719            ε{{{)}}}}ˇ
 5720            ❤️{{{)}}}}ˇ
 5721        "
 5722        .unindent(),
 5723    );
 5724
 5725    // autoclose multi-character pairs
 5726    cx.set_state(
 5727        &"
 5728            ˇ
 5729            ˇ
 5730        "
 5731        .unindent(),
 5732    );
 5733    cx.update_editor(|view, cx| {
 5734        view.handle_input("/", cx);
 5735        view.handle_input("*", cx);
 5736    });
 5737    cx.assert_editor_state(
 5738        &"
 5739            /*ˇ */
 5740            /*ˇ */
 5741        "
 5742        .unindent(),
 5743    );
 5744
 5745    // one cursor autocloses a multi-character pair, one cursor
 5746    // does not autoclose.
 5747    cx.set_state(
 5748        &"
 5749 5750            ˇ
 5751        "
 5752        .unindent(),
 5753    );
 5754    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5755    cx.assert_editor_state(
 5756        &"
 5757            /*ˇ */
 5758 5759        "
 5760        .unindent(),
 5761    );
 5762
 5763    // Don't autoclose if the next character isn't whitespace and isn't
 5764    // listed in the language's "autoclose_before" section.
 5765    cx.set_state("ˇa b");
 5766    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5767    cx.assert_editor_state("{ˇa b");
 5768
 5769    // Don't autoclose if `close` is false for the bracket pair
 5770    cx.set_state("ˇ");
 5771    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5772    cx.assert_editor_state("");
 5773
 5774    // Surround with brackets if text is selected
 5775    cx.set_state("«aˇ» b");
 5776    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5777    cx.assert_editor_state("{«aˇ»} b");
 5778
 5779    // Autclose pair where the start and end characters are the same
 5780    cx.set_state("");
 5781    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5782    cx.assert_editor_state("a\"ˇ\"");
 5783    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5784    cx.assert_editor_state("a\"\"ˇ");
 5785
 5786    // Don't autoclose pair if autoclose is disabled
 5787    cx.set_state("ˇ");
 5788    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5789    cx.assert_editor_state("");
 5790
 5791    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5792    cx.set_state("«aˇ» b");
 5793    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5794    cx.assert_editor_state("<«aˇ»> b");
 5795}
 5796
 5797#[gpui::test]
 5798async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5799    init_test(cx, |settings| {
 5800        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5801    });
 5802
 5803    let mut cx = EditorTestContext::new(cx).await;
 5804
 5805    let language = Arc::new(Language::new(
 5806        LanguageConfig {
 5807            brackets: BracketPairConfig {
 5808                pairs: vec![
 5809                    BracketPair {
 5810                        start: "{".to_string(),
 5811                        end: "}".to_string(),
 5812                        close: true,
 5813                        surround: true,
 5814                        newline: true,
 5815                    },
 5816                    BracketPair {
 5817                        start: "(".to_string(),
 5818                        end: ")".to_string(),
 5819                        close: true,
 5820                        surround: true,
 5821                        newline: true,
 5822                    },
 5823                    BracketPair {
 5824                        start: "[".to_string(),
 5825                        end: "]".to_string(),
 5826                        close: false,
 5827                        surround: false,
 5828                        newline: true,
 5829                    },
 5830                ],
 5831                ..Default::default()
 5832            },
 5833            autoclose_before: "})]".to_string(),
 5834            ..Default::default()
 5835        },
 5836        Some(tree_sitter_rust::LANGUAGE.into()),
 5837    ));
 5838
 5839    cx.language_registry().add(language.clone());
 5840    cx.update_buffer(|buffer, cx| {
 5841        buffer.set_language(Some(language), cx);
 5842    });
 5843
 5844    cx.set_state(
 5845        &"
 5846            ˇ
 5847            ˇ
 5848            ˇ
 5849        "
 5850        .unindent(),
 5851    );
 5852
 5853    // ensure only matching closing brackets are skipped over
 5854    cx.update_editor(|view, cx| {
 5855        view.handle_input("}", cx);
 5856        view.move_left(&MoveLeft, cx);
 5857        view.handle_input(")", cx);
 5858        view.move_left(&MoveLeft, cx);
 5859    });
 5860    cx.assert_editor_state(
 5861        &"
 5862            ˇ)}
 5863            ˇ)}
 5864            ˇ)}
 5865        "
 5866        .unindent(),
 5867    );
 5868
 5869    // skip-over closing brackets at multiple cursors
 5870    cx.update_editor(|view, cx| {
 5871        view.handle_input(")", cx);
 5872        view.handle_input("}", cx);
 5873    });
 5874    cx.assert_editor_state(
 5875        &"
 5876            )}ˇ
 5877            )}ˇ
 5878            )}ˇ
 5879        "
 5880        .unindent(),
 5881    );
 5882
 5883    // ignore non-close brackets
 5884    cx.update_editor(|view, cx| {
 5885        view.handle_input("]", cx);
 5886        view.move_left(&MoveLeft, cx);
 5887        view.handle_input("]", cx);
 5888    });
 5889    cx.assert_editor_state(
 5890        &"
 5891            )}]ˇ]
 5892            )}]ˇ]
 5893            )}]ˇ]
 5894        "
 5895        .unindent(),
 5896    );
 5897}
 5898
 5899#[gpui::test]
 5900async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5901    init_test(cx, |_| {});
 5902
 5903    let mut cx = EditorTestContext::new(cx).await;
 5904
 5905    let html_language = Arc::new(
 5906        Language::new(
 5907            LanguageConfig {
 5908                name: "HTML".into(),
 5909                brackets: BracketPairConfig {
 5910                    pairs: vec![
 5911                        BracketPair {
 5912                            start: "<".into(),
 5913                            end: ">".into(),
 5914                            close: true,
 5915                            ..Default::default()
 5916                        },
 5917                        BracketPair {
 5918                            start: "{".into(),
 5919                            end: "}".into(),
 5920                            close: true,
 5921                            ..Default::default()
 5922                        },
 5923                        BracketPair {
 5924                            start: "(".into(),
 5925                            end: ")".into(),
 5926                            close: true,
 5927                            ..Default::default()
 5928                        },
 5929                    ],
 5930                    ..Default::default()
 5931                },
 5932                autoclose_before: "})]>".into(),
 5933                ..Default::default()
 5934            },
 5935            Some(tree_sitter_html::language()),
 5936        )
 5937        .with_injection_query(
 5938            r#"
 5939            (script_element
 5940                (raw_text) @content
 5941                (#set! "language" "javascript"))
 5942            "#,
 5943        )
 5944        .unwrap(),
 5945    );
 5946
 5947    let javascript_language = Arc::new(Language::new(
 5948        LanguageConfig {
 5949            name: "JavaScript".into(),
 5950            brackets: BracketPairConfig {
 5951                pairs: vec![
 5952                    BracketPair {
 5953                        start: "/*".into(),
 5954                        end: " */".into(),
 5955                        close: true,
 5956                        ..Default::default()
 5957                    },
 5958                    BracketPair {
 5959                        start: "{".into(),
 5960                        end: "}".into(),
 5961                        close: true,
 5962                        ..Default::default()
 5963                    },
 5964                    BracketPair {
 5965                        start: "(".into(),
 5966                        end: ")".into(),
 5967                        close: true,
 5968                        ..Default::default()
 5969                    },
 5970                ],
 5971                ..Default::default()
 5972            },
 5973            autoclose_before: "})]>".into(),
 5974            ..Default::default()
 5975        },
 5976        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5977    ));
 5978
 5979    cx.language_registry().add(html_language.clone());
 5980    cx.language_registry().add(javascript_language.clone());
 5981
 5982    cx.update_buffer(|buffer, cx| {
 5983        buffer.set_language(Some(html_language), cx);
 5984    });
 5985
 5986    cx.set_state(
 5987        &r#"
 5988            <body>ˇ
 5989                <script>
 5990                    var x = 1;ˇ
 5991                </script>
 5992            </body>ˇ
 5993        "#
 5994        .unindent(),
 5995    );
 5996
 5997    // Precondition: different languages are active at different locations.
 5998    cx.update_editor(|editor, cx| {
 5999        let snapshot = editor.snapshot(cx);
 6000        let cursors = editor.selections.ranges::<usize>(cx);
 6001        let languages = cursors
 6002            .iter()
 6003            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6004            .collect::<Vec<_>>();
 6005        assert_eq!(
 6006            languages,
 6007            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6008        );
 6009    });
 6010
 6011    // Angle brackets autoclose in HTML, but not JavaScript.
 6012    cx.update_editor(|editor, cx| {
 6013        editor.handle_input("<", cx);
 6014        editor.handle_input("a", cx);
 6015    });
 6016    cx.assert_editor_state(
 6017        &r#"
 6018            <body><aˇ>
 6019                <script>
 6020                    var x = 1;<aˇ
 6021                </script>
 6022            </body><aˇ>
 6023        "#
 6024        .unindent(),
 6025    );
 6026
 6027    // Curly braces and parens autoclose in both HTML and JavaScript.
 6028    cx.update_editor(|editor, cx| {
 6029        editor.handle_input(" b=", cx);
 6030        editor.handle_input("{", cx);
 6031        editor.handle_input("c", cx);
 6032        editor.handle_input("(", cx);
 6033    });
 6034    cx.assert_editor_state(
 6035        &r#"
 6036            <body><a b={c(ˇ)}>
 6037                <script>
 6038                    var x = 1;<a b={c(ˇ)}
 6039                </script>
 6040            </body><a b={c(ˇ)}>
 6041        "#
 6042        .unindent(),
 6043    );
 6044
 6045    // Brackets that were already autoclosed are skipped.
 6046    cx.update_editor(|editor, cx| {
 6047        editor.handle_input(")", cx);
 6048        editor.handle_input("d", cx);
 6049        editor.handle_input("}", cx);
 6050    });
 6051    cx.assert_editor_state(
 6052        &r#"
 6053            <body><a b={c()d}ˇ>
 6054                <script>
 6055                    var x = 1;<a b={c()d}ˇ
 6056                </script>
 6057            </body><a b={c()d}ˇ>
 6058        "#
 6059        .unindent(),
 6060    );
 6061    cx.update_editor(|editor, cx| {
 6062        editor.handle_input(">", cx);
 6063    });
 6064    cx.assert_editor_state(
 6065        &r#"
 6066            <body><a b={c()d}>ˇ
 6067                <script>
 6068                    var x = 1;<a b={c()d}>ˇ
 6069                </script>
 6070            </body><a b={c()d}>ˇ
 6071        "#
 6072        .unindent(),
 6073    );
 6074
 6075    // Reset
 6076    cx.set_state(
 6077        &r#"
 6078            <body>ˇ
 6079                <script>
 6080                    var x = 1;ˇ
 6081                </script>
 6082            </body>ˇ
 6083        "#
 6084        .unindent(),
 6085    );
 6086
 6087    cx.update_editor(|editor, cx| {
 6088        editor.handle_input("<", cx);
 6089    });
 6090    cx.assert_editor_state(
 6091        &r#"
 6092            <body><ˇ>
 6093                <script>
 6094                    var x = 1;<ˇ
 6095                </script>
 6096            </body><ˇ>
 6097        "#
 6098        .unindent(),
 6099    );
 6100
 6101    // When backspacing, the closing angle brackets are removed.
 6102    cx.update_editor(|editor, cx| {
 6103        editor.backspace(&Backspace, cx);
 6104    });
 6105    cx.assert_editor_state(
 6106        &r#"
 6107            <body>ˇ
 6108                <script>
 6109                    var x = 1;ˇ
 6110                </script>
 6111            </body>ˇ
 6112        "#
 6113        .unindent(),
 6114    );
 6115
 6116    // Block comments autoclose in JavaScript, but not HTML.
 6117    cx.update_editor(|editor, cx| {
 6118        editor.handle_input("/", cx);
 6119        editor.handle_input("*", cx);
 6120    });
 6121    cx.assert_editor_state(
 6122        &r#"
 6123            <body>/*ˇ
 6124                <script>
 6125                    var x = 1;/*ˇ */
 6126                </script>
 6127            </body>/*ˇ
 6128        "#
 6129        .unindent(),
 6130    );
 6131}
 6132
 6133#[gpui::test]
 6134async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6135    init_test(cx, |_| {});
 6136
 6137    let mut cx = EditorTestContext::new(cx).await;
 6138
 6139    let rust_language = Arc::new(
 6140        Language::new(
 6141            LanguageConfig {
 6142                name: "Rust".into(),
 6143                brackets: serde_json::from_value(json!([
 6144                    { "start": "{", "end": "}", "close": true, "newline": true },
 6145                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6146                ]))
 6147                .unwrap(),
 6148                autoclose_before: "})]>".into(),
 6149                ..Default::default()
 6150            },
 6151            Some(tree_sitter_rust::LANGUAGE.into()),
 6152        )
 6153        .with_override_query("(string_literal) @string")
 6154        .unwrap(),
 6155    );
 6156
 6157    cx.language_registry().add(rust_language.clone());
 6158    cx.update_buffer(|buffer, cx| {
 6159        buffer.set_language(Some(rust_language), cx);
 6160    });
 6161
 6162    cx.set_state(
 6163        &r#"
 6164            let x = ˇ
 6165        "#
 6166        .unindent(),
 6167    );
 6168
 6169    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6170    cx.update_editor(|editor, cx| {
 6171        editor.handle_input("\"", cx);
 6172    });
 6173    cx.assert_editor_state(
 6174        &r#"
 6175            let x = "ˇ"
 6176        "#
 6177        .unindent(),
 6178    );
 6179
 6180    // Inserting another quotation mark. The cursor moves across the existing
 6181    // automatically-inserted quotation mark.
 6182    cx.update_editor(|editor, cx| {
 6183        editor.handle_input("\"", cx);
 6184    });
 6185    cx.assert_editor_state(
 6186        &r#"
 6187            let x = ""ˇ
 6188        "#
 6189        .unindent(),
 6190    );
 6191
 6192    // Reset
 6193    cx.set_state(
 6194        &r#"
 6195            let x = ˇ
 6196        "#
 6197        .unindent(),
 6198    );
 6199
 6200    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6201    cx.update_editor(|editor, cx| {
 6202        editor.handle_input("\"", cx);
 6203        editor.handle_input(" ", cx);
 6204        editor.move_left(&Default::default(), cx);
 6205        editor.handle_input("\\", cx);
 6206        editor.handle_input("\"", cx);
 6207    });
 6208    cx.assert_editor_state(
 6209        &r#"
 6210            let x = "\"ˇ "
 6211        "#
 6212        .unindent(),
 6213    );
 6214
 6215    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6216    // mark. Nothing is inserted.
 6217    cx.update_editor(|editor, cx| {
 6218        editor.move_right(&Default::default(), cx);
 6219        editor.handle_input("\"", cx);
 6220    });
 6221    cx.assert_editor_state(
 6222        &r#"
 6223            let x = "\" "ˇ
 6224        "#
 6225        .unindent(),
 6226    );
 6227}
 6228
 6229#[gpui::test]
 6230async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6231    init_test(cx, |_| {});
 6232
 6233    let language = Arc::new(Language::new(
 6234        LanguageConfig {
 6235            brackets: BracketPairConfig {
 6236                pairs: vec![
 6237                    BracketPair {
 6238                        start: "{".to_string(),
 6239                        end: "}".to_string(),
 6240                        close: true,
 6241                        surround: true,
 6242                        newline: true,
 6243                    },
 6244                    BracketPair {
 6245                        start: "/* ".to_string(),
 6246                        end: "*/".to_string(),
 6247                        close: true,
 6248                        surround: true,
 6249                        ..Default::default()
 6250                    },
 6251                ],
 6252                ..Default::default()
 6253            },
 6254            ..Default::default()
 6255        },
 6256        Some(tree_sitter_rust::LANGUAGE.into()),
 6257    ));
 6258
 6259    let text = r#"
 6260        a
 6261        b
 6262        c
 6263    "#
 6264    .unindent();
 6265
 6266    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6267    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6268    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6269    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6270        .await;
 6271
 6272    view.update(cx, |view, cx| {
 6273        view.change_selections(None, cx, |s| {
 6274            s.select_display_ranges([
 6275                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6276                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6277                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6278            ])
 6279        });
 6280
 6281        view.handle_input("{", cx);
 6282        view.handle_input("{", cx);
 6283        view.handle_input("{", cx);
 6284        assert_eq!(
 6285            view.text(cx),
 6286            "
 6287                {{{a}}}
 6288                {{{b}}}
 6289                {{{c}}}
 6290            "
 6291            .unindent()
 6292        );
 6293        assert_eq!(
 6294            view.selections.display_ranges(cx),
 6295            [
 6296                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6297                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6298                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6299            ]
 6300        );
 6301
 6302        view.undo(&Undo, cx);
 6303        view.undo(&Undo, cx);
 6304        view.undo(&Undo, cx);
 6305        assert_eq!(
 6306            view.text(cx),
 6307            "
 6308                a
 6309                b
 6310                c
 6311            "
 6312            .unindent()
 6313        );
 6314        assert_eq!(
 6315            view.selections.display_ranges(cx),
 6316            [
 6317                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6318                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6319                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6320            ]
 6321        );
 6322
 6323        // Ensure inserting the first character of a multi-byte bracket pair
 6324        // doesn't surround the selections with the bracket.
 6325        view.handle_input("/", cx);
 6326        assert_eq!(
 6327            view.text(cx),
 6328            "
 6329                /
 6330                /
 6331                /
 6332            "
 6333            .unindent()
 6334        );
 6335        assert_eq!(
 6336            view.selections.display_ranges(cx),
 6337            [
 6338                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6339                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6340                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6341            ]
 6342        );
 6343
 6344        view.undo(&Undo, cx);
 6345        assert_eq!(
 6346            view.text(cx),
 6347            "
 6348                a
 6349                b
 6350                c
 6351            "
 6352            .unindent()
 6353        );
 6354        assert_eq!(
 6355            view.selections.display_ranges(cx),
 6356            [
 6357                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6358                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6359                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6360            ]
 6361        );
 6362
 6363        // Ensure inserting the last character of a multi-byte bracket pair
 6364        // doesn't surround the selections with the bracket.
 6365        view.handle_input("*", cx);
 6366        assert_eq!(
 6367            view.text(cx),
 6368            "
 6369                *
 6370                *
 6371                *
 6372            "
 6373            .unindent()
 6374        );
 6375        assert_eq!(
 6376            view.selections.display_ranges(cx),
 6377            [
 6378                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6379                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6380                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6381            ]
 6382        );
 6383    });
 6384}
 6385
 6386#[gpui::test]
 6387async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6388    init_test(cx, |_| {});
 6389
 6390    let language = Arc::new(Language::new(
 6391        LanguageConfig {
 6392            brackets: BracketPairConfig {
 6393                pairs: vec![BracketPair {
 6394                    start: "{".to_string(),
 6395                    end: "}".to_string(),
 6396                    close: true,
 6397                    surround: true,
 6398                    newline: true,
 6399                }],
 6400                ..Default::default()
 6401            },
 6402            autoclose_before: "}".to_string(),
 6403            ..Default::default()
 6404        },
 6405        Some(tree_sitter_rust::LANGUAGE.into()),
 6406    ));
 6407
 6408    let text = r#"
 6409        a
 6410        b
 6411        c
 6412    "#
 6413    .unindent();
 6414
 6415    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6416    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6417    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6418    editor
 6419        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6420        .await;
 6421
 6422    editor.update(cx, |editor, cx| {
 6423        editor.change_selections(None, cx, |s| {
 6424            s.select_ranges([
 6425                Point::new(0, 1)..Point::new(0, 1),
 6426                Point::new(1, 1)..Point::new(1, 1),
 6427                Point::new(2, 1)..Point::new(2, 1),
 6428            ])
 6429        });
 6430
 6431        editor.handle_input("{", cx);
 6432        editor.handle_input("{", cx);
 6433        editor.handle_input("_", cx);
 6434        assert_eq!(
 6435            editor.text(cx),
 6436            "
 6437                a{{_}}
 6438                b{{_}}
 6439                c{{_}}
 6440            "
 6441            .unindent()
 6442        );
 6443        assert_eq!(
 6444            editor.selections.ranges::<Point>(cx),
 6445            [
 6446                Point::new(0, 4)..Point::new(0, 4),
 6447                Point::new(1, 4)..Point::new(1, 4),
 6448                Point::new(2, 4)..Point::new(2, 4)
 6449            ]
 6450        );
 6451
 6452        editor.backspace(&Default::default(), cx);
 6453        editor.backspace(&Default::default(), cx);
 6454        assert_eq!(
 6455            editor.text(cx),
 6456            "
 6457                a{}
 6458                b{}
 6459                c{}
 6460            "
 6461            .unindent()
 6462        );
 6463        assert_eq!(
 6464            editor.selections.ranges::<Point>(cx),
 6465            [
 6466                Point::new(0, 2)..Point::new(0, 2),
 6467                Point::new(1, 2)..Point::new(1, 2),
 6468                Point::new(2, 2)..Point::new(2, 2)
 6469            ]
 6470        );
 6471
 6472        editor.delete_to_previous_word_start(&Default::default(), cx);
 6473        assert_eq!(
 6474            editor.text(cx),
 6475            "
 6476                a
 6477                b
 6478                c
 6479            "
 6480            .unindent()
 6481        );
 6482        assert_eq!(
 6483            editor.selections.ranges::<Point>(cx),
 6484            [
 6485                Point::new(0, 1)..Point::new(0, 1),
 6486                Point::new(1, 1)..Point::new(1, 1),
 6487                Point::new(2, 1)..Point::new(2, 1)
 6488            ]
 6489        );
 6490    });
 6491}
 6492
 6493#[gpui::test]
 6494async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6495    init_test(cx, |settings| {
 6496        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6497    });
 6498
 6499    let mut cx = EditorTestContext::new(cx).await;
 6500
 6501    let language = Arc::new(Language::new(
 6502        LanguageConfig {
 6503            brackets: BracketPairConfig {
 6504                pairs: vec![
 6505                    BracketPair {
 6506                        start: "{".to_string(),
 6507                        end: "}".to_string(),
 6508                        close: true,
 6509                        surround: true,
 6510                        newline: true,
 6511                    },
 6512                    BracketPair {
 6513                        start: "(".to_string(),
 6514                        end: ")".to_string(),
 6515                        close: true,
 6516                        surround: true,
 6517                        newline: true,
 6518                    },
 6519                    BracketPair {
 6520                        start: "[".to_string(),
 6521                        end: "]".to_string(),
 6522                        close: false,
 6523                        surround: true,
 6524                        newline: true,
 6525                    },
 6526                ],
 6527                ..Default::default()
 6528            },
 6529            autoclose_before: "})]".to_string(),
 6530            ..Default::default()
 6531        },
 6532        Some(tree_sitter_rust::LANGUAGE.into()),
 6533    ));
 6534
 6535    cx.language_registry().add(language.clone());
 6536    cx.update_buffer(|buffer, cx| {
 6537        buffer.set_language(Some(language), cx);
 6538    });
 6539
 6540    cx.set_state(
 6541        &"
 6542            {(ˇ)}
 6543            [[ˇ]]
 6544            {(ˇ)}
 6545        "
 6546        .unindent(),
 6547    );
 6548
 6549    cx.update_editor(|view, cx| {
 6550        view.backspace(&Default::default(), cx);
 6551        view.backspace(&Default::default(), cx);
 6552    });
 6553
 6554    cx.assert_editor_state(
 6555        &"
 6556            ˇ
 6557            ˇ]]
 6558            ˇ
 6559        "
 6560        .unindent(),
 6561    );
 6562
 6563    cx.update_editor(|view, cx| {
 6564        view.handle_input("{", cx);
 6565        view.handle_input("{", cx);
 6566        view.move_right(&MoveRight, cx);
 6567        view.move_right(&MoveRight, cx);
 6568        view.move_left(&MoveLeft, cx);
 6569        view.move_left(&MoveLeft, cx);
 6570        view.backspace(&Default::default(), cx);
 6571    });
 6572
 6573    cx.assert_editor_state(
 6574        &"
 6575            {ˇ}
 6576            {ˇ}]]
 6577            {ˇ}
 6578        "
 6579        .unindent(),
 6580    );
 6581
 6582    cx.update_editor(|view, cx| {
 6583        view.backspace(&Default::default(), cx);
 6584    });
 6585
 6586    cx.assert_editor_state(
 6587        &"
 6588            ˇ
 6589            ˇ]]
 6590            ˇ
 6591        "
 6592        .unindent(),
 6593    );
 6594}
 6595
 6596#[gpui::test]
 6597async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6598    init_test(cx, |_| {});
 6599
 6600    let language = Arc::new(Language::new(
 6601        LanguageConfig::default(),
 6602        Some(tree_sitter_rust::LANGUAGE.into()),
 6603    ));
 6604
 6605    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6606    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6607    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6608    editor
 6609        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6610        .await;
 6611
 6612    editor.update(cx, |editor, cx| {
 6613        editor.set_auto_replace_emoji_shortcode(true);
 6614
 6615        editor.handle_input("Hello ", cx);
 6616        editor.handle_input(":wave", cx);
 6617        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6618
 6619        editor.handle_input(":", cx);
 6620        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6621
 6622        editor.handle_input(" :smile", cx);
 6623        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6624
 6625        editor.handle_input(":", cx);
 6626        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6627
 6628        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6629        editor.handle_input(":wave", cx);
 6630        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6631
 6632        editor.handle_input(":", cx);
 6633        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6634
 6635        editor.handle_input(":1", cx);
 6636        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6637
 6638        editor.handle_input(":", cx);
 6639        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6640
 6641        // Ensure shortcode does not get replaced when it is part of a word
 6642        editor.handle_input(" Test:wave", cx);
 6643        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6644
 6645        editor.handle_input(":", cx);
 6646        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6647
 6648        editor.set_auto_replace_emoji_shortcode(false);
 6649
 6650        // Ensure shortcode does not get replaced when auto replace is off
 6651        editor.handle_input(" :wave", cx);
 6652        assert_eq!(
 6653            editor.text(cx),
 6654            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6655        );
 6656
 6657        editor.handle_input(":", cx);
 6658        assert_eq!(
 6659            editor.text(cx),
 6660            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6661        );
 6662    });
 6663}
 6664
 6665#[gpui::test]
 6666async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6667    init_test(cx, |_| {});
 6668
 6669    let (text, insertion_ranges) = marked_text_ranges(
 6670        indoc! {"
 6671            ˇ
 6672        "},
 6673        false,
 6674    );
 6675
 6676    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6677    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6678
 6679    _ = editor.update(cx, |editor, cx| {
 6680        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6681
 6682        editor
 6683            .insert_snippet(&insertion_ranges, snippet, cx)
 6684            .unwrap();
 6685
 6686        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6687            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6688            assert_eq!(editor.text(cx), expected_text);
 6689            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6690        }
 6691
 6692        assert(
 6693            editor,
 6694            cx,
 6695            indoc! {"
 6696            type «» =•
 6697            "},
 6698        );
 6699
 6700        assert!(editor.context_menu_visible(), "There should be a matches");
 6701    });
 6702}
 6703
 6704#[gpui::test]
 6705async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6706    init_test(cx, |_| {});
 6707
 6708    let (text, insertion_ranges) = marked_text_ranges(
 6709        indoc! {"
 6710            a.ˇ b
 6711            a.ˇ b
 6712            a.ˇ b
 6713        "},
 6714        false,
 6715    );
 6716
 6717    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6718    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6719
 6720    editor.update(cx, |editor, cx| {
 6721        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6722
 6723        editor
 6724            .insert_snippet(&insertion_ranges, snippet, cx)
 6725            .unwrap();
 6726
 6727        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6728            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6729            assert_eq!(editor.text(cx), expected_text);
 6730            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6731        }
 6732
 6733        assert(
 6734            editor,
 6735            cx,
 6736            indoc! {"
 6737                a.f(«one», two, «three») b
 6738                a.f(«one», two, «three») b
 6739                a.f(«one», two, «three») b
 6740            "},
 6741        );
 6742
 6743        // Can't move earlier than the first tab stop
 6744        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6745        assert(
 6746            editor,
 6747            cx,
 6748            indoc! {"
 6749                a.f(«one», two, «three») b
 6750                a.f(«one», two, «three») b
 6751                a.f(«one», two, «three») b
 6752            "},
 6753        );
 6754
 6755        assert!(editor.move_to_next_snippet_tabstop(cx));
 6756        assert(
 6757            editor,
 6758            cx,
 6759            indoc! {"
 6760                a.f(one, «two», three) b
 6761                a.f(one, «two», three) b
 6762                a.f(one, «two», three) b
 6763            "},
 6764        );
 6765
 6766        editor.move_to_prev_snippet_tabstop(cx);
 6767        assert(
 6768            editor,
 6769            cx,
 6770            indoc! {"
 6771                a.f(«one», two, «three») b
 6772                a.f(«one», two, «three») b
 6773                a.f(«one», two, «three») b
 6774            "},
 6775        );
 6776
 6777        assert!(editor.move_to_next_snippet_tabstop(cx));
 6778        assert(
 6779            editor,
 6780            cx,
 6781            indoc! {"
 6782                a.f(one, «two», three) b
 6783                a.f(one, «two», three) b
 6784                a.f(one, «two», three) b
 6785            "},
 6786        );
 6787        assert!(editor.move_to_next_snippet_tabstop(cx));
 6788        assert(
 6789            editor,
 6790            cx,
 6791            indoc! {"
 6792                a.f(one, two, three)ˇ b
 6793                a.f(one, two, three)ˇ b
 6794                a.f(one, two, three)ˇ b
 6795            "},
 6796        );
 6797
 6798        // As soon as the last tab stop is reached, snippet state is gone
 6799        editor.move_to_prev_snippet_tabstop(cx);
 6800        assert(
 6801            editor,
 6802            cx,
 6803            indoc! {"
 6804                a.f(one, two, three)ˇ b
 6805                a.f(one, two, three)ˇ b
 6806                a.f(one, two, three)ˇ b
 6807            "},
 6808        );
 6809    });
 6810}
 6811
 6812#[gpui::test]
 6813async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6814    init_test(cx, |_| {});
 6815
 6816    let fs = FakeFs::new(cx.executor());
 6817    fs.insert_file("/file.rs", Default::default()).await;
 6818
 6819    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6820
 6821    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6822    language_registry.add(rust_lang());
 6823    let mut fake_servers = language_registry.register_fake_lsp(
 6824        "Rust",
 6825        FakeLspAdapter {
 6826            capabilities: lsp::ServerCapabilities {
 6827                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6828                ..Default::default()
 6829            },
 6830            ..Default::default()
 6831        },
 6832    );
 6833
 6834    let buffer = project
 6835        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6836        .await
 6837        .unwrap();
 6838
 6839    cx.executor().start_waiting();
 6840    let fake_server = fake_servers.next().await.unwrap();
 6841
 6842    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6843    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6844    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6845    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6846
 6847    let save = editor
 6848        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6849        .unwrap();
 6850    fake_server
 6851        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6852            assert_eq!(
 6853                params.text_document.uri,
 6854                lsp::Url::from_file_path("/file.rs").unwrap()
 6855            );
 6856            assert_eq!(params.options.tab_size, 4);
 6857            Ok(Some(vec![lsp::TextEdit::new(
 6858                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6859                ", ".to_string(),
 6860            )]))
 6861        })
 6862        .next()
 6863        .await;
 6864    cx.executor().start_waiting();
 6865    save.await;
 6866
 6867    assert_eq!(
 6868        editor.update(cx, |editor, cx| editor.text(cx)),
 6869        "one, two\nthree\n"
 6870    );
 6871    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6872
 6873    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6874    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6875
 6876    // Ensure we can still save even if formatting hangs.
 6877    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6878        assert_eq!(
 6879            params.text_document.uri,
 6880            lsp::Url::from_file_path("/file.rs").unwrap()
 6881        );
 6882        futures::future::pending::<()>().await;
 6883        unreachable!()
 6884    });
 6885    let save = editor
 6886        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6887        .unwrap();
 6888    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6889    cx.executor().start_waiting();
 6890    save.await;
 6891    assert_eq!(
 6892        editor.update(cx, |editor, cx| editor.text(cx)),
 6893        "one\ntwo\nthree\n"
 6894    );
 6895    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6896
 6897    // For non-dirty buffer, no formatting request should be sent
 6898    let save = editor
 6899        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6900        .unwrap();
 6901    let _pending_format_request = fake_server
 6902        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6903            panic!("Should not be invoked on non-dirty buffer");
 6904        })
 6905        .next();
 6906    cx.executor().start_waiting();
 6907    save.await;
 6908
 6909    // Set rust language override and assert overridden tabsize is sent to language server
 6910    update_test_language_settings(cx, |settings| {
 6911        settings.languages.insert(
 6912            "Rust".into(),
 6913            LanguageSettingsContent {
 6914                tab_size: NonZeroU32::new(8),
 6915                ..Default::default()
 6916            },
 6917        );
 6918    });
 6919
 6920    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6921    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6922    let save = editor
 6923        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6924        .unwrap();
 6925    fake_server
 6926        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6927            assert_eq!(
 6928                params.text_document.uri,
 6929                lsp::Url::from_file_path("/file.rs").unwrap()
 6930            );
 6931            assert_eq!(params.options.tab_size, 8);
 6932            Ok(Some(vec![]))
 6933        })
 6934        .next()
 6935        .await;
 6936    cx.executor().start_waiting();
 6937    save.await;
 6938}
 6939
 6940#[gpui::test]
 6941async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6942    init_test(cx, |_| {});
 6943
 6944    let cols = 4;
 6945    let rows = 10;
 6946    let sample_text_1 = sample_text(rows, cols, 'a');
 6947    assert_eq!(
 6948        sample_text_1,
 6949        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6950    );
 6951    let sample_text_2 = sample_text(rows, cols, 'l');
 6952    assert_eq!(
 6953        sample_text_2,
 6954        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6955    );
 6956    let sample_text_3 = sample_text(rows, cols, 'v');
 6957    assert_eq!(
 6958        sample_text_3,
 6959        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6960    );
 6961
 6962    let fs = FakeFs::new(cx.executor());
 6963    fs.insert_tree(
 6964        "/a",
 6965        json!({
 6966            "main.rs": sample_text_1,
 6967            "other.rs": sample_text_2,
 6968            "lib.rs": sample_text_3,
 6969        }),
 6970    )
 6971    .await;
 6972
 6973    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6974    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6975    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6976
 6977    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6978    language_registry.add(rust_lang());
 6979    let mut fake_servers = language_registry.register_fake_lsp(
 6980        "Rust",
 6981        FakeLspAdapter {
 6982            capabilities: lsp::ServerCapabilities {
 6983                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6984                ..Default::default()
 6985            },
 6986            ..Default::default()
 6987        },
 6988    );
 6989
 6990    let worktree = project.update(cx, |project, cx| {
 6991        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6992        assert_eq!(worktrees.len(), 1);
 6993        worktrees.pop().unwrap()
 6994    });
 6995    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6996
 6997    let buffer_1 = project
 6998        .update(cx, |project, cx| {
 6999            project.open_buffer((worktree_id, "main.rs"), cx)
 7000        })
 7001        .await
 7002        .unwrap();
 7003    let buffer_2 = project
 7004        .update(cx, |project, cx| {
 7005            project.open_buffer((worktree_id, "other.rs"), cx)
 7006        })
 7007        .await
 7008        .unwrap();
 7009    let buffer_3 = project
 7010        .update(cx, |project, cx| {
 7011            project.open_buffer((worktree_id, "lib.rs"), cx)
 7012        })
 7013        .await
 7014        .unwrap();
 7015
 7016    let multi_buffer = cx.new_model(|cx| {
 7017        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7018        multi_buffer.push_excerpts(
 7019            buffer_1.clone(),
 7020            [
 7021                ExcerptRange {
 7022                    context: Point::new(0, 0)..Point::new(3, 0),
 7023                    primary: None,
 7024                },
 7025                ExcerptRange {
 7026                    context: Point::new(5, 0)..Point::new(7, 0),
 7027                    primary: None,
 7028                },
 7029                ExcerptRange {
 7030                    context: Point::new(9, 0)..Point::new(10, 4),
 7031                    primary: None,
 7032                },
 7033            ],
 7034            cx,
 7035        );
 7036        multi_buffer.push_excerpts(
 7037            buffer_2.clone(),
 7038            [
 7039                ExcerptRange {
 7040                    context: Point::new(0, 0)..Point::new(3, 0),
 7041                    primary: None,
 7042                },
 7043                ExcerptRange {
 7044                    context: Point::new(5, 0)..Point::new(7, 0),
 7045                    primary: None,
 7046                },
 7047                ExcerptRange {
 7048                    context: Point::new(9, 0)..Point::new(10, 4),
 7049                    primary: None,
 7050                },
 7051            ],
 7052            cx,
 7053        );
 7054        multi_buffer.push_excerpts(
 7055            buffer_3.clone(),
 7056            [
 7057                ExcerptRange {
 7058                    context: Point::new(0, 0)..Point::new(3, 0),
 7059                    primary: None,
 7060                },
 7061                ExcerptRange {
 7062                    context: Point::new(5, 0)..Point::new(7, 0),
 7063                    primary: None,
 7064                },
 7065                ExcerptRange {
 7066                    context: Point::new(9, 0)..Point::new(10, 4),
 7067                    primary: None,
 7068                },
 7069            ],
 7070            cx,
 7071        );
 7072        multi_buffer
 7073    });
 7074    let multi_buffer_editor = cx.new_view(|cx| {
 7075        Editor::new(
 7076            EditorMode::Full,
 7077            multi_buffer,
 7078            Some(project.clone()),
 7079            true,
 7080            cx,
 7081        )
 7082    });
 7083
 7084    multi_buffer_editor.update(cx, |editor, cx| {
 7085        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7086        editor.insert("|one|two|three|", cx);
 7087    });
 7088    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7089    multi_buffer_editor.update(cx, |editor, cx| {
 7090        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7091            s.select_ranges(Some(60..70))
 7092        });
 7093        editor.insert("|four|five|six|", cx);
 7094    });
 7095    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7096
 7097    // First two buffers should be edited, but not the third one.
 7098    assert_eq!(
 7099        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7100        "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}",
 7101    );
 7102    buffer_1.update(cx, |buffer, _| {
 7103        assert!(buffer.is_dirty());
 7104        assert_eq!(
 7105            buffer.text(),
 7106            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7107        )
 7108    });
 7109    buffer_2.update(cx, |buffer, _| {
 7110        assert!(buffer.is_dirty());
 7111        assert_eq!(
 7112            buffer.text(),
 7113            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7114        )
 7115    });
 7116    buffer_3.update(cx, |buffer, _| {
 7117        assert!(!buffer.is_dirty());
 7118        assert_eq!(buffer.text(), sample_text_3,)
 7119    });
 7120
 7121    cx.executor().start_waiting();
 7122    let save = multi_buffer_editor
 7123        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7124        .unwrap();
 7125
 7126    let fake_server = fake_servers.next().await.unwrap();
 7127    fake_server
 7128        .server
 7129        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7130            Ok(Some(vec![lsp::TextEdit::new(
 7131                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7132                format!("[{} formatted]", params.text_document.uri),
 7133            )]))
 7134        })
 7135        .detach();
 7136    save.await;
 7137
 7138    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7139    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7140    assert_eq!(
 7141        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7142        "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}",
 7143    );
 7144    buffer_1.update(cx, |buffer, _| {
 7145        assert!(!buffer.is_dirty());
 7146        assert_eq!(
 7147            buffer.text(),
 7148            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7149        )
 7150    });
 7151    buffer_2.update(cx, |buffer, _| {
 7152        assert!(!buffer.is_dirty());
 7153        assert_eq!(
 7154            buffer.text(),
 7155            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7156        )
 7157    });
 7158    buffer_3.update(cx, |buffer, _| {
 7159        assert!(!buffer.is_dirty());
 7160        assert_eq!(buffer.text(), sample_text_3,)
 7161    });
 7162}
 7163
 7164#[gpui::test]
 7165async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7166    init_test(cx, |_| {});
 7167
 7168    let fs = FakeFs::new(cx.executor());
 7169    fs.insert_file("/file.rs", Default::default()).await;
 7170
 7171    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7172
 7173    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7174    language_registry.add(rust_lang());
 7175    let mut fake_servers = language_registry.register_fake_lsp(
 7176        "Rust",
 7177        FakeLspAdapter {
 7178            capabilities: lsp::ServerCapabilities {
 7179                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7180                ..Default::default()
 7181            },
 7182            ..Default::default()
 7183        },
 7184    );
 7185
 7186    let buffer = project
 7187        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7188        .await
 7189        .unwrap();
 7190
 7191    cx.executor().start_waiting();
 7192    let fake_server = fake_servers.next().await.unwrap();
 7193
 7194    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7195    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7196    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7197    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7198
 7199    let save = editor
 7200        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7201        .unwrap();
 7202    fake_server
 7203        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7204            assert_eq!(
 7205                params.text_document.uri,
 7206                lsp::Url::from_file_path("/file.rs").unwrap()
 7207            );
 7208            assert_eq!(params.options.tab_size, 4);
 7209            Ok(Some(vec![lsp::TextEdit::new(
 7210                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7211                ", ".to_string(),
 7212            )]))
 7213        })
 7214        .next()
 7215        .await;
 7216    cx.executor().start_waiting();
 7217    save.await;
 7218    assert_eq!(
 7219        editor.update(cx, |editor, cx| editor.text(cx)),
 7220        "one, two\nthree\n"
 7221    );
 7222    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7223
 7224    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7225    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7226
 7227    // Ensure we can still save even if formatting hangs.
 7228    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7229        move |params, _| async move {
 7230            assert_eq!(
 7231                params.text_document.uri,
 7232                lsp::Url::from_file_path("/file.rs").unwrap()
 7233            );
 7234            futures::future::pending::<()>().await;
 7235            unreachable!()
 7236        },
 7237    );
 7238    let save = editor
 7239        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7240        .unwrap();
 7241    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7242    cx.executor().start_waiting();
 7243    save.await;
 7244    assert_eq!(
 7245        editor.update(cx, |editor, cx| editor.text(cx)),
 7246        "one\ntwo\nthree\n"
 7247    );
 7248    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7249
 7250    // For non-dirty buffer, no formatting request should be sent
 7251    let save = editor
 7252        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7253        .unwrap();
 7254    let _pending_format_request = fake_server
 7255        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7256            panic!("Should not be invoked on non-dirty buffer");
 7257        })
 7258        .next();
 7259    cx.executor().start_waiting();
 7260    save.await;
 7261
 7262    // Set Rust language override and assert overridden tabsize is sent to language server
 7263    update_test_language_settings(cx, |settings| {
 7264        settings.languages.insert(
 7265            "Rust".into(),
 7266            LanguageSettingsContent {
 7267                tab_size: NonZeroU32::new(8),
 7268                ..Default::default()
 7269            },
 7270        );
 7271    });
 7272
 7273    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7274    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7275    let save = editor
 7276        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7277        .unwrap();
 7278    fake_server
 7279        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7280            assert_eq!(
 7281                params.text_document.uri,
 7282                lsp::Url::from_file_path("/file.rs").unwrap()
 7283            );
 7284            assert_eq!(params.options.tab_size, 8);
 7285            Ok(Some(vec![]))
 7286        })
 7287        .next()
 7288        .await;
 7289    cx.executor().start_waiting();
 7290    save.await;
 7291}
 7292
 7293#[gpui::test]
 7294async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7295    init_test(cx, |settings| {
 7296        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7297            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7298        ))
 7299    });
 7300
 7301    let fs = FakeFs::new(cx.executor());
 7302    fs.insert_file("/file.rs", Default::default()).await;
 7303
 7304    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7305
 7306    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7307    language_registry.add(Arc::new(Language::new(
 7308        LanguageConfig {
 7309            name: "Rust".into(),
 7310            matcher: LanguageMatcher {
 7311                path_suffixes: vec!["rs".to_string()],
 7312                ..Default::default()
 7313            },
 7314            ..LanguageConfig::default()
 7315        },
 7316        Some(tree_sitter_rust::LANGUAGE.into()),
 7317    )));
 7318    update_test_language_settings(cx, |settings| {
 7319        // Enable Prettier formatting for the same buffer, and ensure
 7320        // LSP is called instead of Prettier.
 7321        settings.defaults.prettier = Some(PrettierSettings {
 7322            allowed: true,
 7323            ..PrettierSettings::default()
 7324        });
 7325    });
 7326    let mut fake_servers = language_registry.register_fake_lsp(
 7327        "Rust",
 7328        FakeLspAdapter {
 7329            capabilities: lsp::ServerCapabilities {
 7330                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7331                ..Default::default()
 7332            },
 7333            ..Default::default()
 7334        },
 7335    );
 7336
 7337    let buffer = project
 7338        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7339        .await
 7340        .unwrap();
 7341
 7342    cx.executor().start_waiting();
 7343    let fake_server = fake_servers.next().await.unwrap();
 7344
 7345    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7346    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7347    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7348
 7349    let format = editor
 7350        .update(cx, |editor, cx| {
 7351            editor.perform_format(
 7352                project.clone(),
 7353                FormatTrigger::Manual,
 7354                FormatTarget::Buffer,
 7355                cx,
 7356            )
 7357        })
 7358        .unwrap();
 7359    fake_server
 7360        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7361            assert_eq!(
 7362                params.text_document.uri,
 7363                lsp::Url::from_file_path("/file.rs").unwrap()
 7364            );
 7365            assert_eq!(params.options.tab_size, 4);
 7366            Ok(Some(vec![lsp::TextEdit::new(
 7367                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7368                ", ".to_string(),
 7369            )]))
 7370        })
 7371        .next()
 7372        .await;
 7373    cx.executor().start_waiting();
 7374    format.await;
 7375    assert_eq!(
 7376        editor.update(cx, |editor, cx| editor.text(cx)),
 7377        "one, two\nthree\n"
 7378    );
 7379
 7380    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7381    // Ensure we don't lock if formatting hangs.
 7382    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7383        assert_eq!(
 7384            params.text_document.uri,
 7385            lsp::Url::from_file_path("/file.rs").unwrap()
 7386        );
 7387        futures::future::pending::<()>().await;
 7388        unreachable!()
 7389    });
 7390    let format = editor
 7391        .update(cx, |editor, cx| {
 7392            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7393        })
 7394        .unwrap();
 7395    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7396    cx.executor().start_waiting();
 7397    format.await;
 7398    assert_eq!(
 7399        editor.update(cx, |editor, cx| editor.text(cx)),
 7400        "one\ntwo\nthree\n"
 7401    );
 7402}
 7403
 7404#[gpui::test]
 7405async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7406    init_test(cx, |_| {});
 7407
 7408    let mut cx = EditorLspTestContext::new_rust(
 7409        lsp::ServerCapabilities {
 7410            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7411            ..Default::default()
 7412        },
 7413        cx,
 7414    )
 7415    .await;
 7416
 7417    cx.set_state(indoc! {"
 7418        one.twoˇ
 7419    "});
 7420
 7421    // The format request takes a long time. When it completes, it inserts
 7422    // a newline and an indent before the `.`
 7423    cx.lsp
 7424        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7425            let executor = cx.background_executor().clone();
 7426            async move {
 7427                executor.timer(Duration::from_millis(100)).await;
 7428                Ok(Some(vec![lsp::TextEdit {
 7429                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7430                    new_text: "\n    ".into(),
 7431                }]))
 7432            }
 7433        });
 7434
 7435    // Submit a format request.
 7436    let format_1 = cx
 7437        .update_editor(|editor, cx| editor.format(&Format, cx))
 7438        .unwrap();
 7439    cx.executor().run_until_parked();
 7440
 7441    // Submit a second format request.
 7442    let format_2 = cx
 7443        .update_editor(|editor, cx| editor.format(&Format, cx))
 7444        .unwrap();
 7445    cx.executor().run_until_parked();
 7446
 7447    // Wait for both format requests to complete
 7448    cx.executor().advance_clock(Duration::from_millis(200));
 7449    cx.executor().start_waiting();
 7450    format_1.await.unwrap();
 7451    cx.executor().start_waiting();
 7452    format_2.await.unwrap();
 7453
 7454    // The formatting edits only happens once.
 7455    cx.assert_editor_state(indoc! {"
 7456        one
 7457            .twoˇ
 7458    "});
 7459}
 7460
 7461#[gpui::test]
 7462async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7463    init_test(cx, |settings| {
 7464        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7465    });
 7466
 7467    let mut cx = EditorLspTestContext::new_rust(
 7468        lsp::ServerCapabilities {
 7469            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7470            ..Default::default()
 7471        },
 7472        cx,
 7473    )
 7474    .await;
 7475
 7476    // Set up a buffer white some trailing whitespace and no trailing newline.
 7477    cx.set_state(
 7478        &[
 7479            "one ",   //
 7480            "twoˇ",   //
 7481            "three ", //
 7482            "four",   //
 7483        ]
 7484        .join("\n"),
 7485    );
 7486
 7487    // Submit a format request.
 7488    let format = cx
 7489        .update_editor(|editor, cx| editor.format(&Format, cx))
 7490        .unwrap();
 7491
 7492    // Record which buffer changes have been sent to the language server
 7493    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7494    cx.lsp
 7495        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7496            let buffer_changes = buffer_changes.clone();
 7497            move |params, _| {
 7498                buffer_changes.lock().extend(
 7499                    params
 7500                        .content_changes
 7501                        .into_iter()
 7502                        .map(|e| (e.range.unwrap(), e.text)),
 7503                );
 7504            }
 7505        });
 7506
 7507    // Handle formatting requests to the language server.
 7508    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7509        let buffer_changes = buffer_changes.clone();
 7510        move |_, _| {
 7511            // When formatting is requested, trailing whitespace has already been stripped,
 7512            // and the trailing newline has already been added.
 7513            assert_eq!(
 7514                &buffer_changes.lock()[1..],
 7515                &[
 7516                    (
 7517                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7518                        "".into()
 7519                    ),
 7520                    (
 7521                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7522                        "".into()
 7523                    ),
 7524                    (
 7525                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7526                        "\n".into()
 7527                    ),
 7528                ]
 7529            );
 7530
 7531            // Insert blank lines between each line of the buffer.
 7532            async move {
 7533                Ok(Some(vec![
 7534                    lsp::TextEdit {
 7535                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7536                        new_text: "\n".into(),
 7537                    },
 7538                    lsp::TextEdit {
 7539                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7540                        new_text: "\n".into(),
 7541                    },
 7542                ]))
 7543            }
 7544        }
 7545    });
 7546
 7547    // After formatting the buffer, the trailing whitespace is stripped,
 7548    // a newline is appended, and the edits provided by the language server
 7549    // have been applied.
 7550    format.await.unwrap();
 7551    cx.assert_editor_state(
 7552        &[
 7553            "one",   //
 7554            "",      //
 7555            "twoˇ",  //
 7556            "",      //
 7557            "three", //
 7558            "four",  //
 7559            "",      //
 7560        ]
 7561        .join("\n"),
 7562    );
 7563
 7564    // Undoing the formatting undoes the trailing whitespace removal, the
 7565    // trailing newline, and the LSP edits.
 7566    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7567    cx.assert_editor_state(
 7568        &[
 7569            "one ",   //
 7570            "twoˇ",   //
 7571            "three ", //
 7572            "four",   //
 7573        ]
 7574        .join("\n"),
 7575    );
 7576}
 7577
 7578#[gpui::test]
 7579async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7580    cx: &mut gpui::TestAppContext,
 7581) {
 7582    init_test(cx, |_| {});
 7583
 7584    cx.update(|cx| {
 7585        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7586            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7587                settings.auto_signature_help = Some(true);
 7588            });
 7589        });
 7590    });
 7591
 7592    let mut cx = EditorLspTestContext::new_rust(
 7593        lsp::ServerCapabilities {
 7594            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7595                ..Default::default()
 7596            }),
 7597            ..Default::default()
 7598        },
 7599        cx,
 7600    )
 7601    .await;
 7602
 7603    let language = Language::new(
 7604        LanguageConfig {
 7605            name: "Rust".into(),
 7606            brackets: BracketPairConfig {
 7607                pairs: vec![
 7608                    BracketPair {
 7609                        start: "{".to_string(),
 7610                        end: "}".to_string(),
 7611                        close: true,
 7612                        surround: true,
 7613                        newline: true,
 7614                    },
 7615                    BracketPair {
 7616                        start: "(".to_string(),
 7617                        end: ")".to_string(),
 7618                        close: true,
 7619                        surround: true,
 7620                        newline: true,
 7621                    },
 7622                    BracketPair {
 7623                        start: "/*".to_string(),
 7624                        end: " */".to_string(),
 7625                        close: true,
 7626                        surround: true,
 7627                        newline: true,
 7628                    },
 7629                    BracketPair {
 7630                        start: "[".to_string(),
 7631                        end: "]".to_string(),
 7632                        close: false,
 7633                        surround: false,
 7634                        newline: true,
 7635                    },
 7636                    BracketPair {
 7637                        start: "\"".to_string(),
 7638                        end: "\"".to_string(),
 7639                        close: true,
 7640                        surround: true,
 7641                        newline: false,
 7642                    },
 7643                    BracketPair {
 7644                        start: "<".to_string(),
 7645                        end: ">".to_string(),
 7646                        close: false,
 7647                        surround: true,
 7648                        newline: true,
 7649                    },
 7650                ],
 7651                ..Default::default()
 7652            },
 7653            autoclose_before: "})]".to_string(),
 7654            ..Default::default()
 7655        },
 7656        Some(tree_sitter_rust::LANGUAGE.into()),
 7657    );
 7658    let language = Arc::new(language);
 7659
 7660    cx.language_registry().add(language.clone());
 7661    cx.update_buffer(|buffer, cx| {
 7662        buffer.set_language(Some(language), cx);
 7663    });
 7664
 7665    cx.set_state(
 7666        &r#"
 7667            fn main() {
 7668                sampleˇ
 7669            }
 7670        "#
 7671        .unindent(),
 7672    );
 7673
 7674    cx.update_editor(|view, cx| {
 7675        view.handle_input("(", cx);
 7676    });
 7677    cx.assert_editor_state(
 7678        &"
 7679            fn main() {
 7680                sample(ˇ)
 7681            }
 7682        "
 7683        .unindent(),
 7684    );
 7685
 7686    let mocked_response = lsp::SignatureHelp {
 7687        signatures: vec![lsp::SignatureInformation {
 7688            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7689            documentation: None,
 7690            parameters: Some(vec![
 7691                lsp::ParameterInformation {
 7692                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7693                    documentation: None,
 7694                },
 7695                lsp::ParameterInformation {
 7696                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7697                    documentation: None,
 7698                },
 7699            ]),
 7700            active_parameter: None,
 7701        }],
 7702        active_signature: Some(0),
 7703        active_parameter: Some(0),
 7704    };
 7705    handle_signature_help_request(&mut cx, mocked_response).await;
 7706
 7707    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7708        .await;
 7709
 7710    cx.editor(|editor, _| {
 7711        let signature_help_state = editor.signature_help_state.popover().cloned();
 7712        assert!(signature_help_state.is_some());
 7713        let ParsedMarkdown {
 7714            text, highlights, ..
 7715        } = signature_help_state.unwrap().parsed_content;
 7716        assert_eq!(text, "param1: u8, param2: u8");
 7717        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7718    });
 7719}
 7720
 7721#[gpui::test]
 7722async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7723    init_test(cx, |_| {});
 7724
 7725    cx.update(|cx| {
 7726        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7727            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7728                settings.auto_signature_help = Some(false);
 7729                settings.show_signature_help_after_edits = Some(false);
 7730            });
 7731        });
 7732    });
 7733
 7734    let mut cx = EditorLspTestContext::new_rust(
 7735        lsp::ServerCapabilities {
 7736            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7737                ..Default::default()
 7738            }),
 7739            ..Default::default()
 7740        },
 7741        cx,
 7742    )
 7743    .await;
 7744
 7745    let language = Language::new(
 7746        LanguageConfig {
 7747            name: "Rust".into(),
 7748            brackets: BracketPairConfig {
 7749                pairs: vec![
 7750                    BracketPair {
 7751                        start: "{".to_string(),
 7752                        end: "}".to_string(),
 7753                        close: true,
 7754                        surround: true,
 7755                        newline: true,
 7756                    },
 7757                    BracketPair {
 7758                        start: "(".to_string(),
 7759                        end: ")".to_string(),
 7760                        close: true,
 7761                        surround: true,
 7762                        newline: true,
 7763                    },
 7764                    BracketPair {
 7765                        start: "/*".to_string(),
 7766                        end: " */".to_string(),
 7767                        close: true,
 7768                        surround: true,
 7769                        newline: true,
 7770                    },
 7771                    BracketPair {
 7772                        start: "[".to_string(),
 7773                        end: "]".to_string(),
 7774                        close: false,
 7775                        surround: false,
 7776                        newline: true,
 7777                    },
 7778                    BracketPair {
 7779                        start: "\"".to_string(),
 7780                        end: "\"".to_string(),
 7781                        close: true,
 7782                        surround: true,
 7783                        newline: false,
 7784                    },
 7785                    BracketPair {
 7786                        start: "<".to_string(),
 7787                        end: ">".to_string(),
 7788                        close: false,
 7789                        surround: true,
 7790                        newline: true,
 7791                    },
 7792                ],
 7793                ..Default::default()
 7794            },
 7795            autoclose_before: "})]".to_string(),
 7796            ..Default::default()
 7797        },
 7798        Some(tree_sitter_rust::LANGUAGE.into()),
 7799    );
 7800    let language = Arc::new(language);
 7801
 7802    cx.language_registry().add(language.clone());
 7803    cx.update_buffer(|buffer, cx| {
 7804        buffer.set_language(Some(language), cx);
 7805    });
 7806
 7807    // Ensure that signature_help is not called when no signature help is enabled.
 7808    cx.set_state(
 7809        &r#"
 7810            fn main() {
 7811                sampleˇ
 7812            }
 7813        "#
 7814        .unindent(),
 7815    );
 7816    cx.update_editor(|view, cx| {
 7817        view.handle_input("(", cx);
 7818    });
 7819    cx.assert_editor_state(
 7820        &"
 7821            fn main() {
 7822                sample(ˇ)
 7823            }
 7824        "
 7825        .unindent(),
 7826    );
 7827    cx.editor(|editor, _| {
 7828        assert!(editor.signature_help_state.task().is_none());
 7829    });
 7830
 7831    let mocked_response = lsp::SignatureHelp {
 7832        signatures: vec![lsp::SignatureInformation {
 7833            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7834            documentation: None,
 7835            parameters: Some(vec![
 7836                lsp::ParameterInformation {
 7837                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7838                    documentation: None,
 7839                },
 7840                lsp::ParameterInformation {
 7841                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7842                    documentation: None,
 7843                },
 7844            ]),
 7845            active_parameter: None,
 7846        }],
 7847        active_signature: Some(0),
 7848        active_parameter: Some(0),
 7849    };
 7850
 7851    // Ensure that signature_help is called when enabled afte edits
 7852    cx.update(|cx| {
 7853        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7854            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7855                settings.auto_signature_help = Some(false);
 7856                settings.show_signature_help_after_edits = Some(true);
 7857            });
 7858        });
 7859    });
 7860    cx.set_state(
 7861        &r#"
 7862            fn main() {
 7863                sampleˇ
 7864            }
 7865        "#
 7866        .unindent(),
 7867    );
 7868    cx.update_editor(|view, cx| {
 7869        view.handle_input("(", cx);
 7870    });
 7871    cx.assert_editor_state(
 7872        &"
 7873            fn main() {
 7874                sample(ˇ)
 7875            }
 7876        "
 7877        .unindent(),
 7878    );
 7879    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7880    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7881        .await;
 7882    cx.update_editor(|editor, _| {
 7883        let signature_help_state = editor.signature_help_state.popover().cloned();
 7884        assert!(signature_help_state.is_some());
 7885        let ParsedMarkdown {
 7886            text, highlights, ..
 7887        } = signature_help_state.unwrap().parsed_content;
 7888        assert_eq!(text, "param1: u8, param2: u8");
 7889        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7890        editor.signature_help_state = SignatureHelpState::default();
 7891    });
 7892
 7893    // Ensure that signature_help is called when auto signature help override is enabled
 7894    cx.update(|cx| {
 7895        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7896            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7897                settings.auto_signature_help = Some(true);
 7898                settings.show_signature_help_after_edits = Some(false);
 7899            });
 7900        });
 7901    });
 7902    cx.set_state(
 7903        &r#"
 7904            fn main() {
 7905                sampleˇ
 7906            }
 7907        "#
 7908        .unindent(),
 7909    );
 7910    cx.update_editor(|view, cx| {
 7911        view.handle_input("(", cx);
 7912    });
 7913    cx.assert_editor_state(
 7914        &"
 7915            fn main() {
 7916                sample(ˇ)
 7917            }
 7918        "
 7919        .unindent(),
 7920    );
 7921    handle_signature_help_request(&mut cx, mocked_response).await;
 7922    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7923        .await;
 7924    cx.editor(|editor, _| {
 7925        let signature_help_state = editor.signature_help_state.popover().cloned();
 7926        assert!(signature_help_state.is_some());
 7927        let ParsedMarkdown {
 7928            text, highlights, ..
 7929        } = signature_help_state.unwrap().parsed_content;
 7930        assert_eq!(text, "param1: u8, param2: u8");
 7931        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7932    });
 7933}
 7934
 7935#[gpui::test]
 7936async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7937    init_test(cx, |_| {});
 7938    cx.update(|cx| {
 7939        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7940            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7941                settings.auto_signature_help = Some(true);
 7942            });
 7943        });
 7944    });
 7945
 7946    let mut cx = EditorLspTestContext::new_rust(
 7947        lsp::ServerCapabilities {
 7948            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7949                ..Default::default()
 7950            }),
 7951            ..Default::default()
 7952        },
 7953        cx,
 7954    )
 7955    .await;
 7956
 7957    // A test that directly calls `show_signature_help`
 7958    cx.update_editor(|editor, cx| {
 7959        editor.show_signature_help(&ShowSignatureHelp, cx);
 7960    });
 7961
 7962    let mocked_response = lsp::SignatureHelp {
 7963        signatures: vec![lsp::SignatureInformation {
 7964            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7965            documentation: None,
 7966            parameters: Some(vec![
 7967                lsp::ParameterInformation {
 7968                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7969                    documentation: None,
 7970                },
 7971                lsp::ParameterInformation {
 7972                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7973                    documentation: None,
 7974                },
 7975            ]),
 7976            active_parameter: None,
 7977        }],
 7978        active_signature: Some(0),
 7979        active_parameter: Some(0),
 7980    };
 7981    handle_signature_help_request(&mut cx, mocked_response).await;
 7982
 7983    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7984        .await;
 7985
 7986    cx.editor(|editor, _| {
 7987        let signature_help_state = editor.signature_help_state.popover().cloned();
 7988        assert!(signature_help_state.is_some());
 7989        let ParsedMarkdown {
 7990            text, highlights, ..
 7991        } = signature_help_state.unwrap().parsed_content;
 7992        assert_eq!(text, "param1: u8, param2: u8");
 7993        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7994    });
 7995
 7996    // When exiting outside from inside the brackets, `signature_help` is closed.
 7997    cx.set_state(indoc! {"
 7998        fn main() {
 7999            sample(ˇ);
 8000        }
 8001
 8002        fn sample(param1: u8, param2: u8) {}
 8003    "});
 8004
 8005    cx.update_editor(|editor, cx| {
 8006        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8007    });
 8008
 8009    let mocked_response = lsp::SignatureHelp {
 8010        signatures: Vec::new(),
 8011        active_signature: None,
 8012        active_parameter: None,
 8013    };
 8014    handle_signature_help_request(&mut cx, mocked_response).await;
 8015
 8016    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8017        .await;
 8018
 8019    cx.editor(|editor, _| {
 8020        assert!(!editor.signature_help_state.is_shown());
 8021    });
 8022
 8023    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8024    cx.set_state(indoc! {"
 8025        fn main() {
 8026            sample(ˇ);
 8027        }
 8028
 8029        fn sample(param1: u8, param2: u8) {}
 8030    "});
 8031
 8032    let mocked_response = lsp::SignatureHelp {
 8033        signatures: vec![lsp::SignatureInformation {
 8034            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8035            documentation: None,
 8036            parameters: Some(vec![
 8037                lsp::ParameterInformation {
 8038                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8039                    documentation: None,
 8040                },
 8041                lsp::ParameterInformation {
 8042                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8043                    documentation: None,
 8044                },
 8045            ]),
 8046            active_parameter: None,
 8047        }],
 8048        active_signature: Some(0),
 8049        active_parameter: Some(0),
 8050    };
 8051    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8052    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8053        .await;
 8054    cx.editor(|editor, _| {
 8055        assert!(editor.signature_help_state.is_shown());
 8056    });
 8057
 8058    // Restore the popover with more parameter input
 8059    cx.set_state(indoc! {"
 8060        fn main() {
 8061            sample(param1, param2ˇ);
 8062        }
 8063
 8064        fn sample(param1: u8, param2: u8) {}
 8065    "});
 8066
 8067    let mocked_response = lsp::SignatureHelp {
 8068        signatures: vec![lsp::SignatureInformation {
 8069            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8070            documentation: None,
 8071            parameters: Some(vec![
 8072                lsp::ParameterInformation {
 8073                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8074                    documentation: None,
 8075                },
 8076                lsp::ParameterInformation {
 8077                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8078                    documentation: None,
 8079                },
 8080            ]),
 8081            active_parameter: None,
 8082        }],
 8083        active_signature: Some(0),
 8084        active_parameter: Some(1),
 8085    };
 8086    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8087    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8088        .await;
 8089
 8090    // When selecting a range, the popover is gone.
 8091    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8092    cx.update_editor(|editor, cx| {
 8093        editor.change_selections(None, cx, |s| {
 8094            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8095        })
 8096    });
 8097    cx.assert_editor_state(indoc! {"
 8098        fn main() {
 8099            sample(param1, «ˇparam2»);
 8100        }
 8101
 8102        fn sample(param1: u8, param2: u8) {}
 8103    "});
 8104    cx.editor(|editor, _| {
 8105        assert!(!editor.signature_help_state.is_shown());
 8106    });
 8107
 8108    // When unselecting again, the popover is back if within the brackets.
 8109    cx.update_editor(|editor, cx| {
 8110        editor.change_selections(None, cx, |s| {
 8111            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8112        })
 8113    });
 8114    cx.assert_editor_state(indoc! {"
 8115        fn main() {
 8116            sample(param1, ˇparam2);
 8117        }
 8118
 8119        fn sample(param1: u8, param2: u8) {}
 8120    "});
 8121    handle_signature_help_request(&mut cx, mocked_response).await;
 8122    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8123        .await;
 8124    cx.editor(|editor, _| {
 8125        assert!(editor.signature_help_state.is_shown());
 8126    });
 8127
 8128    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8129    cx.update_editor(|editor, cx| {
 8130        editor.change_selections(None, cx, |s| {
 8131            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8132            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8133        })
 8134    });
 8135    cx.assert_editor_state(indoc! {"
 8136        fn main() {
 8137            sample(param1, ˇparam2);
 8138        }
 8139
 8140        fn sample(param1: u8, param2: u8) {}
 8141    "});
 8142
 8143    let mocked_response = lsp::SignatureHelp {
 8144        signatures: vec![lsp::SignatureInformation {
 8145            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8146            documentation: None,
 8147            parameters: Some(vec![
 8148                lsp::ParameterInformation {
 8149                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8150                    documentation: None,
 8151                },
 8152                lsp::ParameterInformation {
 8153                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8154                    documentation: None,
 8155                },
 8156            ]),
 8157            active_parameter: None,
 8158        }],
 8159        active_signature: Some(0),
 8160        active_parameter: Some(1),
 8161    };
 8162    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8163    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8164        .await;
 8165    cx.update_editor(|editor, cx| {
 8166        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8167    });
 8168    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8169        .await;
 8170    cx.update_editor(|editor, cx| {
 8171        editor.change_selections(None, cx, |s| {
 8172            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8173        })
 8174    });
 8175    cx.assert_editor_state(indoc! {"
 8176        fn main() {
 8177            sample(param1, «ˇparam2»);
 8178        }
 8179
 8180        fn sample(param1: u8, param2: u8) {}
 8181    "});
 8182    cx.update_editor(|editor, cx| {
 8183        editor.change_selections(None, cx, |s| {
 8184            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8185        })
 8186    });
 8187    cx.assert_editor_state(indoc! {"
 8188        fn main() {
 8189            sample(param1, ˇparam2);
 8190        }
 8191
 8192        fn sample(param1: u8, param2: u8) {}
 8193    "});
 8194    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8195        .await;
 8196}
 8197
 8198#[gpui::test]
 8199async fn test_completion(cx: &mut gpui::TestAppContext) {
 8200    init_test(cx, |_| {});
 8201
 8202    let mut cx = EditorLspTestContext::new_rust(
 8203        lsp::ServerCapabilities {
 8204            completion_provider: Some(lsp::CompletionOptions {
 8205                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8206                resolve_provider: Some(true),
 8207                ..Default::default()
 8208            }),
 8209            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8210            ..Default::default()
 8211        },
 8212        cx,
 8213    )
 8214    .await;
 8215    let counter = Arc::new(AtomicUsize::new(0));
 8216
 8217    cx.set_state(indoc! {"
 8218        oneˇ
 8219        two
 8220        three
 8221    "});
 8222    cx.simulate_keystroke(".");
 8223    handle_completion_request(
 8224        &mut cx,
 8225        indoc! {"
 8226            one.|<>
 8227            two
 8228            three
 8229        "},
 8230        vec!["first_completion", "second_completion"],
 8231        counter.clone(),
 8232    )
 8233    .await;
 8234    cx.condition(|editor, _| editor.context_menu_visible())
 8235        .await;
 8236    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8237
 8238    let _handler = handle_signature_help_request(
 8239        &mut cx,
 8240        lsp::SignatureHelp {
 8241            signatures: vec![lsp::SignatureInformation {
 8242                label: "test signature".to_string(),
 8243                documentation: None,
 8244                parameters: Some(vec![lsp::ParameterInformation {
 8245                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8246                    documentation: None,
 8247                }]),
 8248                active_parameter: None,
 8249            }],
 8250            active_signature: None,
 8251            active_parameter: None,
 8252        },
 8253    );
 8254    cx.update_editor(|editor, cx| {
 8255        assert!(
 8256            !editor.signature_help_state.is_shown(),
 8257            "No signature help was called for"
 8258        );
 8259        editor.show_signature_help(&ShowSignatureHelp, cx);
 8260    });
 8261    cx.run_until_parked();
 8262    cx.update_editor(|editor, _| {
 8263        assert!(
 8264            !editor.signature_help_state.is_shown(),
 8265            "No signature help should be shown when completions menu is open"
 8266        );
 8267    });
 8268
 8269    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8270        editor.context_menu_next(&Default::default(), cx);
 8271        editor
 8272            .confirm_completion(&ConfirmCompletion::default(), cx)
 8273            .unwrap()
 8274    });
 8275    cx.assert_editor_state(indoc! {"
 8276        one.second_completionˇ
 8277        two
 8278        three
 8279    "});
 8280
 8281    handle_resolve_completion_request(
 8282        &mut cx,
 8283        Some(vec![
 8284            (
 8285                //This overlaps with the primary completion edit which is
 8286                //misbehavior from the LSP spec, test that we filter it out
 8287                indoc! {"
 8288                    one.second_ˇcompletion
 8289                    two
 8290                    threeˇ
 8291                "},
 8292                "overlapping additional edit",
 8293            ),
 8294            (
 8295                indoc! {"
 8296                    one.second_completion
 8297                    two
 8298                    threeˇ
 8299                "},
 8300                "\nadditional edit",
 8301            ),
 8302        ]),
 8303    )
 8304    .await;
 8305    apply_additional_edits.await.unwrap();
 8306    cx.assert_editor_state(indoc! {"
 8307        one.second_completionˇ
 8308        two
 8309        three
 8310        additional edit
 8311    "});
 8312
 8313    cx.set_state(indoc! {"
 8314        one.second_completion
 8315        twoˇ
 8316        threeˇ
 8317        additional edit
 8318    "});
 8319    cx.simulate_keystroke(" ");
 8320    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8321    cx.simulate_keystroke("s");
 8322    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8323
 8324    cx.assert_editor_state(indoc! {"
 8325        one.second_completion
 8326        two sˇ
 8327        three sˇ
 8328        additional edit
 8329    "});
 8330    handle_completion_request(
 8331        &mut cx,
 8332        indoc! {"
 8333            one.second_completion
 8334            two s
 8335            three <s|>
 8336            additional edit
 8337        "},
 8338        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8339        counter.clone(),
 8340    )
 8341    .await;
 8342    cx.condition(|editor, _| editor.context_menu_visible())
 8343        .await;
 8344    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8345
 8346    cx.simulate_keystroke("i");
 8347
 8348    handle_completion_request(
 8349        &mut cx,
 8350        indoc! {"
 8351            one.second_completion
 8352            two si
 8353            three <si|>
 8354            additional edit
 8355        "},
 8356        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8357        counter.clone(),
 8358    )
 8359    .await;
 8360    cx.condition(|editor, _| editor.context_menu_visible())
 8361        .await;
 8362    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8363
 8364    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8365        editor
 8366            .confirm_completion(&ConfirmCompletion::default(), cx)
 8367            .unwrap()
 8368    });
 8369    cx.assert_editor_state(indoc! {"
 8370        one.second_completion
 8371        two sixth_completionˇ
 8372        three sixth_completionˇ
 8373        additional edit
 8374    "});
 8375
 8376    handle_resolve_completion_request(&mut cx, None).await;
 8377    apply_additional_edits.await.unwrap();
 8378
 8379    update_test_language_settings(&mut cx, |settings| {
 8380        settings.defaults.show_completions_on_input = Some(false);
 8381    });
 8382    cx.set_state("editorˇ");
 8383    cx.simulate_keystroke(".");
 8384    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8385    cx.simulate_keystroke("c");
 8386    cx.simulate_keystroke("l");
 8387    cx.simulate_keystroke("o");
 8388    cx.assert_editor_state("editor.cloˇ");
 8389    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8390    cx.update_editor(|editor, cx| {
 8391        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8392    });
 8393    handle_completion_request(
 8394        &mut cx,
 8395        "editor.<clo|>",
 8396        vec!["close", "clobber"],
 8397        counter.clone(),
 8398    )
 8399    .await;
 8400    cx.condition(|editor, _| editor.context_menu_visible())
 8401        .await;
 8402    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8403
 8404    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8405        editor
 8406            .confirm_completion(&ConfirmCompletion::default(), cx)
 8407            .unwrap()
 8408    });
 8409    cx.assert_editor_state("editor.closeˇ");
 8410    handle_resolve_completion_request(&mut cx, None).await;
 8411    apply_additional_edits.await.unwrap();
 8412}
 8413
 8414#[gpui::test]
 8415async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8416    init_test(cx, |_| {});
 8417    let mut cx = EditorLspTestContext::new_rust(
 8418        lsp::ServerCapabilities {
 8419            completion_provider: Some(lsp::CompletionOptions {
 8420                trigger_characters: Some(vec![".".to_string()]),
 8421                ..Default::default()
 8422            }),
 8423            ..Default::default()
 8424        },
 8425        cx,
 8426    )
 8427    .await;
 8428    cx.lsp
 8429        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8430            Ok(Some(lsp::CompletionResponse::Array(vec![
 8431                lsp::CompletionItem {
 8432                    label: "first".into(),
 8433                    ..Default::default()
 8434                },
 8435                lsp::CompletionItem {
 8436                    label: "last".into(),
 8437                    ..Default::default()
 8438                },
 8439            ])))
 8440        });
 8441    cx.set_state("variableˇ");
 8442    cx.simulate_keystroke(".");
 8443    cx.executor().run_until_parked();
 8444
 8445    cx.update_editor(|editor, _| {
 8446        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8447            assert_eq!(
 8448                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8449                &["first", "last"]
 8450            );
 8451        } else {
 8452            panic!("expected completion menu to be open");
 8453        }
 8454    });
 8455
 8456    cx.update_editor(|editor, cx| {
 8457        editor.move_page_down(&MovePageDown::default(), cx);
 8458        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8459            assert!(
 8460                menu.selected_item == 1,
 8461                "expected PageDown to select the last item from the context menu"
 8462            );
 8463        } else {
 8464            panic!("expected completion menu to stay open after PageDown");
 8465        }
 8466    });
 8467
 8468    cx.update_editor(|editor, cx| {
 8469        editor.move_page_up(&MovePageUp::default(), cx);
 8470        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8471            assert!(
 8472                menu.selected_item == 0,
 8473                "expected PageUp to select the first item from the context menu"
 8474            );
 8475        } else {
 8476            panic!("expected completion menu to stay open after PageUp");
 8477        }
 8478    });
 8479}
 8480
 8481#[gpui::test]
 8482async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8483    init_test(cx, |_| {});
 8484    let mut cx = EditorLspTestContext::new_rust(
 8485        lsp::ServerCapabilities {
 8486            completion_provider: Some(lsp::CompletionOptions {
 8487                trigger_characters: Some(vec![".".to_string()]),
 8488                ..Default::default()
 8489            }),
 8490            ..Default::default()
 8491        },
 8492        cx,
 8493    )
 8494    .await;
 8495    cx.lsp
 8496        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8497            Ok(Some(lsp::CompletionResponse::Array(vec![
 8498                lsp::CompletionItem {
 8499                    label: "Range".into(),
 8500                    sort_text: Some("a".into()),
 8501                    ..Default::default()
 8502                },
 8503                lsp::CompletionItem {
 8504                    label: "r".into(),
 8505                    sort_text: Some("b".into()),
 8506                    ..Default::default()
 8507                },
 8508                lsp::CompletionItem {
 8509                    label: "ret".into(),
 8510                    sort_text: Some("c".into()),
 8511                    ..Default::default()
 8512                },
 8513                lsp::CompletionItem {
 8514                    label: "return".into(),
 8515                    sort_text: Some("d".into()),
 8516                    ..Default::default()
 8517                },
 8518                lsp::CompletionItem {
 8519                    label: "slice".into(),
 8520                    sort_text: Some("d".into()),
 8521                    ..Default::default()
 8522                },
 8523            ])))
 8524        });
 8525    cx.set_state("");
 8526    cx.executor().run_until_parked();
 8527    cx.update_editor(|editor, cx| {
 8528        editor.show_completions(
 8529            &ShowCompletions {
 8530                trigger: Some("r".into()),
 8531            },
 8532            cx,
 8533        );
 8534    });
 8535    cx.executor().run_until_parked();
 8536
 8537    cx.update_editor(|editor, _| {
 8538        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8539            assert_eq!(
 8540                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8541                &["r", "ret", "Range", "return"]
 8542            );
 8543        } else {
 8544            panic!("expected completion menu to be open");
 8545        }
 8546    });
 8547}
 8548
 8549#[gpui::test]
 8550async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8551    init_test(cx, |_| {});
 8552
 8553    let mut cx = EditorLspTestContext::new_rust(
 8554        lsp::ServerCapabilities {
 8555            completion_provider: Some(lsp::CompletionOptions {
 8556                trigger_characters: Some(vec![".".to_string()]),
 8557                resolve_provider: Some(true),
 8558                ..Default::default()
 8559            }),
 8560            ..Default::default()
 8561        },
 8562        cx,
 8563    )
 8564    .await;
 8565
 8566    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8567    cx.simulate_keystroke(".");
 8568    let completion_item = lsp::CompletionItem {
 8569        label: "Some".into(),
 8570        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8571        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8572        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8573            kind: lsp::MarkupKind::Markdown,
 8574            value: "```rust\nSome(2)\n```".to_string(),
 8575        })),
 8576        deprecated: Some(false),
 8577        sort_text: Some("Some".to_string()),
 8578        filter_text: Some("Some".to_string()),
 8579        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8580        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8581            range: lsp::Range {
 8582                start: lsp::Position {
 8583                    line: 0,
 8584                    character: 22,
 8585                },
 8586                end: lsp::Position {
 8587                    line: 0,
 8588                    character: 22,
 8589                },
 8590            },
 8591            new_text: "Some(2)".to_string(),
 8592        })),
 8593        additional_text_edits: Some(vec![lsp::TextEdit {
 8594            range: lsp::Range {
 8595                start: lsp::Position {
 8596                    line: 0,
 8597                    character: 20,
 8598                },
 8599                end: lsp::Position {
 8600                    line: 0,
 8601                    character: 22,
 8602                },
 8603            },
 8604            new_text: "".to_string(),
 8605        }]),
 8606        ..Default::default()
 8607    };
 8608
 8609    let closure_completion_item = completion_item.clone();
 8610    let counter = Arc::new(AtomicUsize::new(0));
 8611    let counter_clone = counter.clone();
 8612    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8613        let task_completion_item = closure_completion_item.clone();
 8614        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8615        async move {
 8616            Ok(Some(lsp::CompletionResponse::Array(vec![
 8617                task_completion_item,
 8618            ])))
 8619        }
 8620    });
 8621
 8622    cx.condition(|editor, _| editor.context_menu_visible())
 8623        .await;
 8624    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8625    assert!(request.next().await.is_some());
 8626    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8627
 8628    cx.simulate_keystroke("S");
 8629    cx.simulate_keystroke("o");
 8630    cx.simulate_keystroke("m");
 8631    cx.condition(|editor, _| editor.context_menu_visible())
 8632        .await;
 8633    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8634    assert!(request.next().await.is_some());
 8635    assert!(request.next().await.is_some());
 8636    assert!(request.next().await.is_some());
 8637    request.close();
 8638    assert!(request.next().await.is_none());
 8639    assert_eq!(
 8640        counter.load(atomic::Ordering::Acquire),
 8641        4,
 8642        "With the completions menu open, only one LSP request should happen per input"
 8643    );
 8644}
 8645
 8646#[gpui::test]
 8647async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8648    init_test(cx, |_| {});
 8649    let mut cx = EditorTestContext::new(cx).await;
 8650    let language = Arc::new(Language::new(
 8651        LanguageConfig {
 8652            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8653            ..Default::default()
 8654        },
 8655        Some(tree_sitter_rust::LANGUAGE.into()),
 8656    ));
 8657    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8658
 8659    // If multiple selections intersect a line, the line is only toggled once.
 8660    cx.set_state(indoc! {"
 8661        fn a() {
 8662            «//b();
 8663            ˇ»// «c();
 8664            //ˇ»  d();
 8665        }
 8666    "});
 8667
 8668    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8669
 8670    cx.assert_editor_state(indoc! {"
 8671        fn a() {
 8672            «b();
 8673            c();
 8674            ˇ» d();
 8675        }
 8676    "});
 8677
 8678    // The comment prefix is inserted at the same column for every line in a
 8679    // selection.
 8680    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8681
 8682    cx.assert_editor_state(indoc! {"
 8683        fn a() {
 8684            // «b();
 8685            // c();
 8686            ˇ»//  d();
 8687        }
 8688    "});
 8689
 8690    // If a selection ends at the beginning of a line, that line is not toggled.
 8691    cx.set_selections_state(indoc! {"
 8692        fn a() {
 8693            // b();
 8694            «// c();
 8695        ˇ»    //  d();
 8696        }
 8697    "});
 8698
 8699    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8700
 8701    cx.assert_editor_state(indoc! {"
 8702        fn a() {
 8703            // b();
 8704            «c();
 8705        ˇ»    //  d();
 8706        }
 8707    "});
 8708
 8709    // If a selection span a single line and is empty, the line is toggled.
 8710    cx.set_state(indoc! {"
 8711        fn a() {
 8712            a();
 8713            b();
 8714        ˇ
 8715        }
 8716    "});
 8717
 8718    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8719
 8720    cx.assert_editor_state(indoc! {"
 8721        fn a() {
 8722            a();
 8723            b();
 8724        //•ˇ
 8725        }
 8726    "});
 8727
 8728    // If a selection span multiple lines, empty lines are not toggled.
 8729    cx.set_state(indoc! {"
 8730        fn a() {
 8731            «a();
 8732
 8733            c();ˇ»
 8734        }
 8735    "});
 8736
 8737    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8738
 8739    cx.assert_editor_state(indoc! {"
 8740        fn a() {
 8741            // «a();
 8742
 8743            // c();ˇ»
 8744        }
 8745    "});
 8746
 8747    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8748    cx.set_state(indoc! {"
 8749        fn a() {
 8750            «// a();
 8751            /// b();
 8752            //! c();ˇ»
 8753        }
 8754    "});
 8755
 8756    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8757
 8758    cx.assert_editor_state(indoc! {"
 8759        fn a() {
 8760            «a();
 8761            b();
 8762            c();ˇ»
 8763        }
 8764    "});
 8765}
 8766
 8767#[gpui::test]
 8768async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8769    init_test(cx, |_| {});
 8770    let mut cx = EditorTestContext::new(cx).await;
 8771    let language = Arc::new(Language::new(
 8772        LanguageConfig {
 8773            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8774            ..Default::default()
 8775        },
 8776        Some(tree_sitter_rust::LANGUAGE.into()),
 8777    ));
 8778    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8779
 8780    let toggle_comments = &ToggleComments {
 8781        advance_downwards: false,
 8782        ignore_indent: true,
 8783    };
 8784
 8785    // If multiple selections intersect a line, the line is only toggled once.
 8786    cx.set_state(indoc! {"
 8787        fn a() {
 8788        //    «b();
 8789        //    c();
 8790        //    ˇ» d();
 8791        }
 8792    "});
 8793
 8794    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8795
 8796    cx.assert_editor_state(indoc! {"
 8797        fn a() {
 8798            «b();
 8799            c();
 8800            ˇ» d();
 8801        }
 8802    "});
 8803
 8804    // The comment prefix is inserted at the beginning of each line
 8805    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8806
 8807    cx.assert_editor_state(indoc! {"
 8808        fn a() {
 8809        //    «b();
 8810        //    c();
 8811        //    ˇ» d();
 8812        }
 8813    "});
 8814
 8815    // If a selection ends at the beginning of a line, that line is not toggled.
 8816    cx.set_selections_state(indoc! {"
 8817        fn a() {
 8818        //    b();
 8819        //    «c();
 8820        ˇ»//     d();
 8821        }
 8822    "});
 8823
 8824    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8825
 8826    cx.assert_editor_state(indoc! {"
 8827        fn a() {
 8828        //    b();
 8829            «c();
 8830        ˇ»//     d();
 8831        }
 8832    "});
 8833
 8834    // If a selection span a single line and is empty, the line is toggled.
 8835    cx.set_state(indoc! {"
 8836        fn a() {
 8837            a();
 8838            b();
 8839        ˇ
 8840        }
 8841    "});
 8842
 8843    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8844
 8845    cx.assert_editor_state(indoc! {"
 8846        fn a() {
 8847            a();
 8848            b();
 8849        //ˇ
 8850        }
 8851    "});
 8852
 8853    // If a selection span multiple lines, empty lines are not toggled.
 8854    cx.set_state(indoc! {"
 8855        fn a() {
 8856            «a();
 8857
 8858            c();ˇ»
 8859        }
 8860    "});
 8861
 8862    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8863
 8864    cx.assert_editor_state(indoc! {"
 8865        fn a() {
 8866        //    «a();
 8867
 8868        //    c();ˇ»
 8869        }
 8870    "});
 8871
 8872    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8873    cx.set_state(indoc! {"
 8874        fn a() {
 8875        //    «a();
 8876        ///    b();
 8877        //!    c();ˇ»
 8878        }
 8879    "});
 8880
 8881    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8882
 8883    cx.assert_editor_state(indoc! {"
 8884        fn a() {
 8885            «a();
 8886            b();
 8887            c();ˇ»
 8888        }
 8889    "});
 8890}
 8891
 8892#[gpui::test]
 8893async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8894    init_test(cx, |_| {});
 8895
 8896    let language = Arc::new(Language::new(
 8897        LanguageConfig {
 8898            line_comments: vec!["// ".into()],
 8899            ..Default::default()
 8900        },
 8901        Some(tree_sitter_rust::LANGUAGE.into()),
 8902    ));
 8903
 8904    let mut cx = EditorTestContext::new(cx).await;
 8905
 8906    cx.language_registry().add(language.clone());
 8907    cx.update_buffer(|buffer, cx| {
 8908        buffer.set_language(Some(language), cx);
 8909    });
 8910
 8911    let toggle_comments = &ToggleComments {
 8912        advance_downwards: true,
 8913        ignore_indent: false,
 8914    };
 8915
 8916    // Single cursor on one line -> advance
 8917    // Cursor moves horizontally 3 characters as well on non-blank line
 8918    cx.set_state(indoc!(
 8919        "fn a() {
 8920             ˇdog();
 8921             cat();
 8922        }"
 8923    ));
 8924    cx.update_editor(|editor, cx| {
 8925        editor.toggle_comments(toggle_comments, cx);
 8926    });
 8927    cx.assert_editor_state(indoc!(
 8928        "fn a() {
 8929             // dog();
 8930             catˇ();
 8931        }"
 8932    ));
 8933
 8934    // Single selection on one line -> don't advance
 8935    cx.set_state(indoc!(
 8936        "fn a() {
 8937             «dog()ˇ»;
 8938             cat();
 8939        }"
 8940    ));
 8941    cx.update_editor(|editor, cx| {
 8942        editor.toggle_comments(toggle_comments, cx);
 8943    });
 8944    cx.assert_editor_state(indoc!(
 8945        "fn a() {
 8946             // «dog()ˇ»;
 8947             cat();
 8948        }"
 8949    ));
 8950
 8951    // Multiple cursors on one line -> advance
 8952    cx.set_state(indoc!(
 8953        "fn a() {
 8954             ˇdˇog();
 8955             cat();
 8956        }"
 8957    ));
 8958    cx.update_editor(|editor, cx| {
 8959        editor.toggle_comments(toggle_comments, cx);
 8960    });
 8961    cx.assert_editor_state(indoc!(
 8962        "fn a() {
 8963             // dog();
 8964             catˇ(ˇ);
 8965        }"
 8966    ));
 8967
 8968    // Multiple cursors on one line, with selection -> don't advance
 8969    cx.set_state(indoc!(
 8970        "fn a() {
 8971             ˇdˇog«()ˇ»;
 8972             cat();
 8973        }"
 8974    ));
 8975    cx.update_editor(|editor, cx| {
 8976        editor.toggle_comments(toggle_comments, cx);
 8977    });
 8978    cx.assert_editor_state(indoc!(
 8979        "fn a() {
 8980             // ˇdˇog«()ˇ»;
 8981             cat();
 8982        }"
 8983    ));
 8984
 8985    // Single cursor on one line -> advance
 8986    // Cursor moves to column 0 on blank line
 8987    cx.set_state(indoc!(
 8988        "fn a() {
 8989             ˇdog();
 8990
 8991             cat();
 8992        }"
 8993    ));
 8994    cx.update_editor(|editor, cx| {
 8995        editor.toggle_comments(toggle_comments, cx);
 8996    });
 8997    cx.assert_editor_state(indoc!(
 8998        "fn a() {
 8999             // dog();
 9000        ˇ
 9001             cat();
 9002        }"
 9003    ));
 9004
 9005    // Single cursor on one line -> advance
 9006    // Cursor starts and ends at column 0
 9007    cx.set_state(indoc!(
 9008        "fn a() {
 9009         ˇ    dog();
 9010             cat();
 9011        }"
 9012    ));
 9013    cx.update_editor(|editor, cx| {
 9014        editor.toggle_comments(toggle_comments, cx);
 9015    });
 9016    cx.assert_editor_state(indoc!(
 9017        "fn a() {
 9018             // dog();
 9019         ˇ    cat();
 9020        }"
 9021    ));
 9022}
 9023
 9024#[gpui::test]
 9025async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9026    init_test(cx, |_| {});
 9027
 9028    let mut cx = EditorTestContext::new(cx).await;
 9029
 9030    let html_language = Arc::new(
 9031        Language::new(
 9032            LanguageConfig {
 9033                name: "HTML".into(),
 9034                block_comment: Some(("<!-- ".into(), " -->".into())),
 9035                ..Default::default()
 9036            },
 9037            Some(tree_sitter_html::language()),
 9038        )
 9039        .with_injection_query(
 9040            r#"
 9041            (script_element
 9042                (raw_text) @content
 9043                (#set! "language" "javascript"))
 9044            "#,
 9045        )
 9046        .unwrap(),
 9047    );
 9048
 9049    let javascript_language = Arc::new(Language::new(
 9050        LanguageConfig {
 9051            name: "JavaScript".into(),
 9052            line_comments: vec!["// ".into()],
 9053            ..Default::default()
 9054        },
 9055        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9056    ));
 9057
 9058    cx.language_registry().add(html_language.clone());
 9059    cx.language_registry().add(javascript_language.clone());
 9060    cx.update_buffer(|buffer, cx| {
 9061        buffer.set_language(Some(html_language), cx);
 9062    });
 9063
 9064    // Toggle comments for empty selections
 9065    cx.set_state(
 9066        &r#"
 9067            <p>A</p>ˇ
 9068            <p>B</p>ˇ
 9069            <p>C</p>ˇ
 9070        "#
 9071        .unindent(),
 9072    );
 9073    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9074    cx.assert_editor_state(
 9075        &r#"
 9076            <!-- <p>A</p>ˇ -->
 9077            <!-- <p>B</p>ˇ -->
 9078            <!-- <p>C</p>ˇ -->
 9079        "#
 9080        .unindent(),
 9081    );
 9082    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9083    cx.assert_editor_state(
 9084        &r#"
 9085            <p>A</p>ˇ
 9086            <p>B</p>ˇ
 9087            <p>C</p>ˇ
 9088        "#
 9089        .unindent(),
 9090    );
 9091
 9092    // Toggle comments for mixture of empty and non-empty selections, where
 9093    // multiple selections occupy a given line.
 9094    cx.set_state(
 9095        &r#"
 9096            <p>A«</p>
 9097            <p>ˇ»B</p>ˇ
 9098            <p>C«</p>
 9099            <p>ˇ»D</p>ˇ
 9100        "#
 9101        .unindent(),
 9102    );
 9103
 9104    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9105    cx.assert_editor_state(
 9106        &r#"
 9107            <!-- <p>A«</p>
 9108            <p>ˇ»B</p>ˇ -->
 9109            <!-- <p>C«</p>
 9110            <p>ˇ»D</p>ˇ -->
 9111        "#
 9112        .unindent(),
 9113    );
 9114    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9115    cx.assert_editor_state(
 9116        &r#"
 9117            <p>A«</p>
 9118            <p>ˇ»B</p>ˇ
 9119            <p>C«</p>
 9120            <p>ˇ»D</p>ˇ
 9121        "#
 9122        .unindent(),
 9123    );
 9124
 9125    // Toggle comments when different languages are active for different
 9126    // selections.
 9127    cx.set_state(
 9128        &r#"
 9129            ˇ<script>
 9130                ˇvar x = new Y();
 9131            ˇ</script>
 9132        "#
 9133        .unindent(),
 9134    );
 9135    cx.executor().run_until_parked();
 9136    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9137    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9138    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9139    cx.assert_editor_state(
 9140        &r#"
 9141            <!-- ˇ<script> -->
 9142                // ˇvar x = new Y();
 9143            // ˇ</script>
 9144        "#
 9145        .unindent(),
 9146    );
 9147}
 9148
 9149#[gpui::test]
 9150fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9151    init_test(cx, |_| {});
 9152
 9153    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9154    let multibuffer = cx.new_model(|cx| {
 9155        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9156        multibuffer.push_excerpts(
 9157            buffer.clone(),
 9158            [
 9159                ExcerptRange {
 9160                    context: Point::new(0, 0)..Point::new(0, 4),
 9161                    primary: None,
 9162                },
 9163                ExcerptRange {
 9164                    context: Point::new(1, 0)..Point::new(1, 4),
 9165                    primary: None,
 9166                },
 9167            ],
 9168            cx,
 9169        );
 9170        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9171        multibuffer
 9172    });
 9173
 9174    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9175    view.update(cx, |view, cx| {
 9176        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9177        view.change_selections(None, cx, |s| {
 9178            s.select_ranges([
 9179                Point::new(0, 0)..Point::new(0, 0),
 9180                Point::new(1, 0)..Point::new(1, 0),
 9181            ])
 9182        });
 9183
 9184        view.handle_input("X", cx);
 9185        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9186        assert_eq!(
 9187            view.selections.ranges(cx),
 9188            [
 9189                Point::new(0, 1)..Point::new(0, 1),
 9190                Point::new(1, 1)..Point::new(1, 1),
 9191            ]
 9192        );
 9193
 9194        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9195        view.change_selections(None, cx, |s| {
 9196            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9197        });
 9198        view.backspace(&Default::default(), cx);
 9199        assert_eq!(view.text(cx), "Xa\nbbb");
 9200        assert_eq!(
 9201            view.selections.ranges(cx),
 9202            [Point::new(1, 0)..Point::new(1, 0)]
 9203        );
 9204
 9205        view.change_selections(None, cx, |s| {
 9206            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9207        });
 9208        view.backspace(&Default::default(), cx);
 9209        assert_eq!(view.text(cx), "X\nbb");
 9210        assert_eq!(
 9211            view.selections.ranges(cx),
 9212            [Point::new(0, 1)..Point::new(0, 1)]
 9213        );
 9214    });
 9215}
 9216
 9217#[gpui::test]
 9218fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9219    init_test(cx, |_| {});
 9220
 9221    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9222    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9223        indoc! {"
 9224            [aaaa
 9225            (bbbb]
 9226            cccc)",
 9227        },
 9228        markers.clone(),
 9229    );
 9230    let excerpt_ranges = markers.into_iter().map(|marker| {
 9231        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9232        ExcerptRange {
 9233            context,
 9234            primary: None,
 9235        }
 9236    });
 9237    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9238    let multibuffer = cx.new_model(|cx| {
 9239        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9240        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9241        multibuffer
 9242    });
 9243
 9244    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9245    view.update(cx, |view, cx| {
 9246        let (expected_text, selection_ranges) = marked_text_ranges(
 9247            indoc! {"
 9248                aaaa
 9249                bˇbbb
 9250                bˇbbˇb
 9251                cccc"
 9252            },
 9253            true,
 9254        );
 9255        assert_eq!(view.text(cx), expected_text);
 9256        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9257
 9258        view.handle_input("X", cx);
 9259
 9260        let (expected_text, expected_selections) = marked_text_ranges(
 9261            indoc! {"
 9262                aaaa
 9263                bXˇbbXb
 9264                bXˇbbXˇb
 9265                cccc"
 9266            },
 9267            false,
 9268        );
 9269        assert_eq!(view.text(cx), expected_text);
 9270        assert_eq!(view.selections.ranges(cx), expected_selections);
 9271
 9272        view.newline(&Newline, cx);
 9273        let (expected_text, expected_selections) = marked_text_ranges(
 9274            indoc! {"
 9275                aaaa
 9276                bX
 9277                ˇbbX
 9278                b
 9279                bX
 9280                ˇbbX
 9281                ˇb
 9282                cccc"
 9283            },
 9284            false,
 9285        );
 9286        assert_eq!(view.text(cx), expected_text);
 9287        assert_eq!(view.selections.ranges(cx), expected_selections);
 9288    });
 9289}
 9290
 9291#[gpui::test]
 9292fn test_refresh_selections(cx: &mut TestAppContext) {
 9293    init_test(cx, |_| {});
 9294
 9295    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9296    let mut excerpt1_id = None;
 9297    let multibuffer = cx.new_model(|cx| {
 9298        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9299        excerpt1_id = multibuffer
 9300            .push_excerpts(
 9301                buffer.clone(),
 9302                [
 9303                    ExcerptRange {
 9304                        context: Point::new(0, 0)..Point::new(1, 4),
 9305                        primary: None,
 9306                    },
 9307                    ExcerptRange {
 9308                        context: Point::new(1, 0)..Point::new(2, 4),
 9309                        primary: None,
 9310                    },
 9311                ],
 9312                cx,
 9313            )
 9314            .into_iter()
 9315            .next();
 9316        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9317        multibuffer
 9318    });
 9319
 9320    let editor = cx.add_window(|cx| {
 9321        let mut editor = build_editor(multibuffer.clone(), cx);
 9322        let snapshot = editor.snapshot(cx);
 9323        editor.change_selections(None, cx, |s| {
 9324            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9325        });
 9326        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9327        assert_eq!(
 9328            editor.selections.ranges(cx),
 9329            [
 9330                Point::new(1, 3)..Point::new(1, 3),
 9331                Point::new(2, 1)..Point::new(2, 1),
 9332            ]
 9333        );
 9334        editor
 9335    });
 9336
 9337    // Refreshing selections is a no-op when excerpts haven't changed.
 9338    _ = editor.update(cx, |editor, cx| {
 9339        editor.change_selections(None, cx, |s| s.refresh());
 9340        assert_eq!(
 9341            editor.selections.ranges(cx),
 9342            [
 9343                Point::new(1, 3)..Point::new(1, 3),
 9344                Point::new(2, 1)..Point::new(2, 1),
 9345            ]
 9346        );
 9347    });
 9348
 9349    multibuffer.update(cx, |multibuffer, cx| {
 9350        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9351    });
 9352    _ = editor.update(cx, |editor, cx| {
 9353        // Removing an excerpt causes the first selection to become degenerate.
 9354        assert_eq!(
 9355            editor.selections.ranges(cx),
 9356            [
 9357                Point::new(0, 0)..Point::new(0, 0),
 9358                Point::new(0, 1)..Point::new(0, 1)
 9359            ]
 9360        );
 9361
 9362        // Refreshing selections will relocate the first selection to the original buffer
 9363        // location.
 9364        editor.change_selections(None, cx, |s| s.refresh());
 9365        assert_eq!(
 9366            editor.selections.ranges(cx),
 9367            [
 9368                Point::new(0, 1)..Point::new(0, 1),
 9369                Point::new(0, 3)..Point::new(0, 3)
 9370            ]
 9371        );
 9372        assert!(editor.selections.pending_anchor().is_some());
 9373    });
 9374}
 9375
 9376#[gpui::test]
 9377fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9378    init_test(cx, |_| {});
 9379
 9380    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9381    let mut excerpt1_id = None;
 9382    let multibuffer = cx.new_model(|cx| {
 9383        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9384        excerpt1_id = multibuffer
 9385            .push_excerpts(
 9386                buffer.clone(),
 9387                [
 9388                    ExcerptRange {
 9389                        context: Point::new(0, 0)..Point::new(1, 4),
 9390                        primary: None,
 9391                    },
 9392                    ExcerptRange {
 9393                        context: Point::new(1, 0)..Point::new(2, 4),
 9394                        primary: None,
 9395                    },
 9396                ],
 9397                cx,
 9398            )
 9399            .into_iter()
 9400            .next();
 9401        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9402        multibuffer
 9403    });
 9404
 9405    let editor = cx.add_window(|cx| {
 9406        let mut editor = build_editor(multibuffer.clone(), cx);
 9407        let snapshot = editor.snapshot(cx);
 9408        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9409        assert_eq!(
 9410            editor.selections.ranges(cx),
 9411            [Point::new(1, 3)..Point::new(1, 3)]
 9412        );
 9413        editor
 9414    });
 9415
 9416    multibuffer.update(cx, |multibuffer, cx| {
 9417        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9418    });
 9419    _ = editor.update(cx, |editor, cx| {
 9420        assert_eq!(
 9421            editor.selections.ranges(cx),
 9422            [Point::new(0, 0)..Point::new(0, 0)]
 9423        );
 9424
 9425        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9426        editor.change_selections(None, cx, |s| s.refresh());
 9427        assert_eq!(
 9428            editor.selections.ranges(cx),
 9429            [Point::new(0, 3)..Point::new(0, 3)]
 9430        );
 9431        assert!(editor.selections.pending_anchor().is_some());
 9432    });
 9433}
 9434
 9435#[gpui::test]
 9436async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9437    init_test(cx, |_| {});
 9438
 9439    let language = Arc::new(
 9440        Language::new(
 9441            LanguageConfig {
 9442                brackets: BracketPairConfig {
 9443                    pairs: vec![
 9444                        BracketPair {
 9445                            start: "{".to_string(),
 9446                            end: "}".to_string(),
 9447                            close: true,
 9448                            surround: true,
 9449                            newline: true,
 9450                        },
 9451                        BracketPair {
 9452                            start: "/* ".to_string(),
 9453                            end: " */".to_string(),
 9454                            close: true,
 9455                            surround: true,
 9456                            newline: true,
 9457                        },
 9458                    ],
 9459                    ..Default::default()
 9460                },
 9461                ..Default::default()
 9462            },
 9463            Some(tree_sitter_rust::LANGUAGE.into()),
 9464        )
 9465        .with_indents_query("")
 9466        .unwrap(),
 9467    );
 9468
 9469    let text = concat!(
 9470        "{   }\n",     //
 9471        "  x\n",       //
 9472        "  /*   */\n", //
 9473        "x\n",         //
 9474        "{{} }\n",     //
 9475    );
 9476
 9477    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9478    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9479    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9480    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9481        .await;
 9482
 9483    view.update(cx, |view, cx| {
 9484        view.change_selections(None, cx, |s| {
 9485            s.select_display_ranges([
 9486                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9487                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9488                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9489            ])
 9490        });
 9491        view.newline(&Newline, cx);
 9492
 9493        assert_eq!(
 9494            view.buffer().read(cx).read(cx).text(),
 9495            concat!(
 9496                "{ \n",    // Suppress rustfmt
 9497                "\n",      //
 9498                "}\n",     //
 9499                "  x\n",   //
 9500                "  /* \n", //
 9501                "  \n",    //
 9502                "  */\n",  //
 9503                "x\n",     //
 9504                "{{} \n",  //
 9505                "}\n",     //
 9506            )
 9507        );
 9508    });
 9509}
 9510
 9511#[gpui::test]
 9512fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9513    init_test(cx, |_| {});
 9514
 9515    let editor = cx.add_window(|cx| {
 9516        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9517        build_editor(buffer.clone(), cx)
 9518    });
 9519
 9520    _ = editor.update(cx, |editor, cx| {
 9521        struct Type1;
 9522        struct Type2;
 9523
 9524        let buffer = editor.buffer.read(cx).snapshot(cx);
 9525
 9526        let anchor_range =
 9527            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9528
 9529        editor.highlight_background::<Type1>(
 9530            &[
 9531                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9532                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9533                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9534                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9535            ],
 9536            |_| Hsla::red(),
 9537            cx,
 9538        );
 9539        editor.highlight_background::<Type2>(
 9540            &[
 9541                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9542                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9543                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9544                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9545            ],
 9546            |_| Hsla::green(),
 9547            cx,
 9548        );
 9549
 9550        let snapshot = editor.snapshot(cx);
 9551        let mut highlighted_ranges = editor.background_highlights_in_range(
 9552            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9553            &snapshot,
 9554            cx.theme().colors(),
 9555        );
 9556        // Enforce a consistent ordering based on color without relying on the ordering of the
 9557        // highlight's `TypeId` which is non-executor.
 9558        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9559        assert_eq!(
 9560            highlighted_ranges,
 9561            &[
 9562                (
 9563                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9564                    Hsla::red(),
 9565                ),
 9566                (
 9567                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9568                    Hsla::red(),
 9569                ),
 9570                (
 9571                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9572                    Hsla::green(),
 9573                ),
 9574                (
 9575                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9576                    Hsla::green(),
 9577                ),
 9578            ]
 9579        );
 9580        assert_eq!(
 9581            editor.background_highlights_in_range(
 9582                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9583                &snapshot,
 9584                cx.theme().colors(),
 9585            ),
 9586            &[(
 9587                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9588                Hsla::red(),
 9589            )]
 9590        );
 9591    });
 9592}
 9593
 9594#[gpui::test]
 9595async fn test_following(cx: &mut gpui::TestAppContext) {
 9596    init_test(cx, |_| {});
 9597
 9598    let fs = FakeFs::new(cx.executor());
 9599    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9600
 9601    let buffer = project.update(cx, |project, cx| {
 9602        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9603        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9604    });
 9605    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9606    let follower = cx.update(|cx| {
 9607        cx.open_window(
 9608            WindowOptions {
 9609                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9610                    gpui::Point::new(px(0.), px(0.)),
 9611                    gpui::Point::new(px(10.), px(80.)),
 9612                ))),
 9613                ..Default::default()
 9614            },
 9615            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9616        )
 9617        .unwrap()
 9618    });
 9619
 9620    let is_still_following = Rc::new(RefCell::new(true));
 9621    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9622    let pending_update = Rc::new(RefCell::new(None));
 9623    _ = follower.update(cx, {
 9624        let update = pending_update.clone();
 9625        let is_still_following = is_still_following.clone();
 9626        let follower_edit_event_count = follower_edit_event_count.clone();
 9627        |_, cx| {
 9628            cx.subscribe(
 9629                &leader.root_view(cx).unwrap(),
 9630                move |_, leader, event, cx| {
 9631                    leader
 9632                        .read(cx)
 9633                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9634                },
 9635            )
 9636            .detach();
 9637
 9638            cx.subscribe(
 9639                &follower.root_view(cx).unwrap(),
 9640                move |_, _, event: &EditorEvent, _cx| {
 9641                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9642                        *is_still_following.borrow_mut() = false;
 9643                    }
 9644
 9645                    if let EditorEvent::BufferEdited = event {
 9646                        *follower_edit_event_count.borrow_mut() += 1;
 9647                    }
 9648                },
 9649            )
 9650            .detach();
 9651        }
 9652    });
 9653
 9654    // Update the selections only
 9655    _ = leader.update(cx, |leader, cx| {
 9656        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9657    });
 9658    follower
 9659        .update(cx, |follower, cx| {
 9660            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9661        })
 9662        .unwrap()
 9663        .await
 9664        .unwrap();
 9665    _ = follower.update(cx, |follower, cx| {
 9666        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9667    });
 9668    assert!(*is_still_following.borrow());
 9669    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9670
 9671    // Update the scroll position only
 9672    _ = leader.update(cx, |leader, cx| {
 9673        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9674    });
 9675    follower
 9676        .update(cx, |follower, cx| {
 9677            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9678        })
 9679        .unwrap()
 9680        .await
 9681        .unwrap();
 9682    assert_eq!(
 9683        follower
 9684            .update(cx, |follower, cx| follower.scroll_position(cx))
 9685            .unwrap(),
 9686        gpui::Point::new(1.5, 3.5)
 9687    );
 9688    assert!(*is_still_following.borrow());
 9689    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9690
 9691    // Update the selections and scroll position. The follower's scroll position is updated
 9692    // via autoscroll, not via the leader's exact scroll position.
 9693    _ = leader.update(cx, |leader, cx| {
 9694        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9695        leader.request_autoscroll(Autoscroll::newest(), cx);
 9696        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9697    });
 9698    follower
 9699        .update(cx, |follower, cx| {
 9700            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9701        })
 9702        .unwrap()
 9703        .await
 9704        .unwrap();
 9705    _ = follower.update(cx, |follower, cx| {
 9706        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9707        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9708    });
 9709    assert!(*is_still_following.borrow());
 9710
 9711    // Creating a pending selection that precedes another selection
 9712    _ = leader.update(cx, |leader, cx| {
 9713        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9714        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9715    });
 9716    follower
 9717        .update(cx, |follower, cx| {
 9718            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9719        })
 9720        .unwrap()
 9721        .await
 9722        .unwrap();
 9723    _ = follower.update(cx, |follower, cx| {
 9724        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9725    });
 9726    assert!(*is_still_following.borrow());
 9727
 9728    // Extend the pending selection so that it surrounds another selection
 9729    _ = leader.update(cx, |leader, cx| {
 9730        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9731    });
 9732    follower
 9733        .update(cx, |follower, cx| {
 9734            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9735        })
 9736        .unwrap()
 9737        .await
 9738        .unwrap();
 9739    _ = follower.update(cx, |follower, cx| {
 9740        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9741    });
 9742
 9743    // Scrolling locally breaks the follow
 9744    _ = follower.update(cx, |follower, cx| {
 9745        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9746        follower.set_scroll_anchor(
 9747            ScrollAnchor {
 9748                anchor: top_anchor,
 9749                offset: gpui::Point::new(0.0, 0.5),
 9750            },
 9751            cx,
 9752        );
 9753    });
 9754    assert!(!(*is_still_following.borrow()));
 9755}
 9756
 9757#[gpui::test]
 9758async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9759    init_test(cx, |_| {});
 9760
 9761    let fs = FakeFs::new(cx.executor());
 9762    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9763    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9764    let pane = workspace
 9765        .update(cx, |workspace, _| workspace.active_pane().clone())
 9766        .unwrap();
 9767
 9768    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9769
 9770    let leader = pane.update(cx, |_, cx| {
 9771        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9772        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9773    });
 9774
 9775    // Start following the editor when it has no excerpts.
 9776    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9777    let follower_1 = cx
 9778        .update_window(*workspace.deref(), |_, cx| {
 9779            Editor::from_state_proto(
 9780                workspace.root_view(cx).unwrap(),
 9781                ViewId {
 9782                    creator: Default::default(),
 9783                    id: 0,
 9784                },
 9785                &mut state_message,
 9786                cx,
 9787            )
 9788        })
 9789        .unwrap()
 9790        .unwrap()
 9791        .await
 9792        .unwrap();
 9793
 9794    let update_message = Rc::new(RefCell::new(None));
 9795    follower_1.update(cx, {
 9796        let update = update_message.clone();
 9797        |_, cx| {
 9798            cx.subscribe(&leader, move |_, leader, event, cx| {
 9799                leader
 9800                    .read(cx)
 9801                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9802            })
 9803            .detach();
 9804        }
 9805    });
 9806
 9807    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9808        (
 9809            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9810            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9811        )
 9812    });
 9813
 9814    // Insert some excerpts.
 9815    leader.update(cx, |leader, cx| {
 9816        leader.buffer.update(cx, |multibuffer, cx| {
 9817            let excerpt_ids = multibuffer.push_excerpts(
 9818                buffer_1.clone(),
 9819                [
 9820                    ExcerptRange {
 9821                        context: 1..6,
 9822                        primary: None,
 9823                    },
 9824                    ExcerptRange {
 9825                        context: 12..15,
 9826                        primary: None,
 9827                    },
 9828                    ExcerptRange {
 9829                        context: 0..3,
 9830                        primary: None,
 9831                    },
 9832                ],
 9833                cx,
 9834            );
 9835            multibuffer.insert_excerpts_after(
 9836                excerpt_ids[0],
 9837                buffer_2.clone(),
 9838                [
 9839                    ExcerptRange {
 9840                        context: 8..12,
 9841                        primary: None,
 9842                    },
 9843                    ExcerptRange {
 9844                        context: 0..6,
 9845                        primary: None,
 9846                    },
 9847                ],
 9848                cx,
 9849            );
 9850        });
 9851    });
 9852
 9853    // Apply the update of adding the excerpts.
 9854    follower_1
 9855        .update(cx, |follower, cx| {
 9856            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9857        })
 9858        .await
 9859        .unwrap();
 9860    assert_eq!(
 9861        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9862        leader.update(cx, |editor, cx| editor.text(cx))
 9863    );
 9864    update_message.borrow_mut().take();
 9865
 9866    // Start following separately after it already has excerpts.
 9867    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9868    let follower_2 = cx
 9869        .update_window(*workspace.deref(), |_, cx| {
 9870            Editor::from_state_proto(
 9871                workspace.root_view(cx).unwrap().clone(),
 9872                ViewId {
 9873                    creator: Default::default(),
 9874                    id: 0,
 9875                },
 9876                &mut state_message,
 9877                cx,
 9878            )
 9879        })
 9880        .unwrap()
 9881        .unwrap()
 9882        .await
 9883        .unwrap();
 9884    assert_eq!(
 9885        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9886        leader.update(cx, |editor, cx| editor.text(cx))
 9887    );
 9888
 9889    // Remove some excerpts.
 9890    leader.update(cx, |leader, cx| {
 9891        leader.buffer.update(cx, |multibuffer, cx| {
 9892            let excerpt_ids = multibuffer.excerpt_ids();
 9893            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9894            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9895        });
 9896    });
 9897
 9898    // Apply the update of removing the excerpts.
 9899    follower_1
 9900        .update(cx, |follower, cx| {
 9901            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9902        })
 9903        .await
 9904        .unwrap();
 9905    follower_2
 9906        .update(cx, |follower, cx| {
 9907            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9908        })
 9909        .await
 9910        .unwrap();
 9911    update_message.borrow_mut().take();
 9912    assert_eq!(
 9913        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9914        leader.update(cx, |editor, cx| editor.text(cx))
 9915    );
 9916}
 9917
 9918#[gpui::test]
 9919async fn go_to_prev_overlapping_diagnostic(
 9920    executor: BackgroundExecutor,
 9921    cx: &mut gpui::TestAppContext,
 9922) {
 9923    init_test(cx, |_| {});
 9924
 9925    let mut cx = EditorTestContext::new(cx).await;
 9926    let lsp_store =
 9927        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
 9928
 9929    cx.set_state(indoc! {"
 9930        ˇfn func(abc def: i32) -> u32 {
 9931        }
 9932    "});
 9933
 9934    cx.update(|cx| {
 9935        lsp_store.update(cx, |lsp_store, cx| {
 9936            lsp_store
 9937                .update_diagnostics(
 9938                    LanguageServerId(0),
 9939                    lsp::PublishDiagnosticsParams {
 9940                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9941                        version: None,
 9942                        diagnostics: vec![
 9943                            lsp::Diagnostic {
 9944                                range: lsp::Range::new(
 9945                                    lsp::Position::new(0, 11),
 9946                                    lsp::Position::new(0, 12),
 9947                                ),
 9948                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9949                                ..Default::default()
 9950                            },
 9951                            lsp::Diagnostic {
 9952                                range: lsp::Range::new(
 9953                                    lsp::Position::new(0, 12),
 9954                                    lsp::Position::new(0, 15),
 9955                                ),
 9956                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9957                                ..Default::default()
 9958                            },
 9959                            lsp::Diagnostic {
 9960                                range: lsp::Range::new(
 9961                                    lsp::Position::new(0, 25),
 9962                                    lsp::Position::new(0, 28),
 9963                                ),
 9964                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9965                                ..Default::default()
 9966                            },
 9967                        ],
 9968                    },
 9969                    &[],
 9970                    cx,
 9971                )
 9972                .unwrap()
 9973        });
 9974    });
 9975
 9976    executor.run_until_parked();
 9977
 9978    cx.update_editor(|editor, cx| {
 9979        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9980    });
 9981
 9982    cx.assert_editor_state(indoc! {"
 9983        fn func(abc def: i32) -> ˇu32 {
 9984        }
 9985    "});
 9986
 9987    cx.update_editor(|editor, cx| {
 9988        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9989    });
 9990
 9991    cx.assert_editor_state(indoc! {"
 9992        fn func(abc ˇdef: i32) -> u32 {
 9993        }
 9994    "});
 9995
 9996    cx.update_editor(|editor, cx| {
 9997        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9998    });
 9999
10000    cx.assert_editor_state(indoc! {"
10001        fn func(abcˇ def: i32) -> u32 {
10002        }
10003    "});
10004
10005    cx.update_editor(|editor, cx| {
10006        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10007    });
10008
10009    cx.assert_editor_state(indoc! {"
10010        fn func(abc def: i32) -> ˇu32 {
10011        }
10012    "});
10013}
10014
10015#[gpui::test]
10016async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10017    init_test(cx, |_| {});
10018
10019    let mut cx = EditorTestContext::new(cx).await;
10020
10021    cx.set_state(indoc! {"
10022        fn func(abˇc def: i32) -> u32 {
10023        }
10024    "});
10025    let lsp_store =
10026        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10027
10028    cx.update(|cx| {
10029        lsp_store.update(cx, |lsp_store, cx| {
10030            lsp_store.update_diagnostics(
10031                LanguageServerId(0),
10032                lsp::PublishDiagnosticsParams {
10033                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10034                    version: None,
10035                    diagnostics: vec![lsp::Diagnostic {
10036                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10037                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10038                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10039                        ..Default::default()
10040                    }],
10041                },
10042                &[],
10043                cx,
10044            )
10045        })
10046    }).unwrap();
10047    cx.run_until_parked();
10048    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10049    cx.run_until_parked();
10050    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10051}
10052
10053#[gpui::test]
10054async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10055    init_test(cx, |_| {});
10056
10057    let mut cx = EditorTestContext::new(cx).await;
10058
10059    let diff_base = r#"
10060        use some::mod;
10061
10062        const A: u32 = 42;
10063
10064        fn main() {
10065            println!("hello");
10066
10067            println!("world");
10068        }
10069        "#
10070    .unindent();
10071
10072    // Edits are modified, removed, modified, added
10073    cx.set_state(
10074        &r#"
10075        use some::modified;
10076
10077        ˇ
10078        fn main() {
10079            println!("hello there");
10080
10081            println!("around the");
10082            println!("world");
10083        }
10084        "#
10085        .unindent(),
10086    );
10087
10088    cx.set_diff_base(&diff_base);
10089    executor.run_until_parked();
10090
10091    cx.update_editor(|editor, cx| {
10092        //Wrap around the bottom of the buffer
10093        for _ in 0..3 {
10094            editor.go_to_next_hunk(&GoToHunk, cx);
10095        }
10096    });
10097
10098    cx.assert_editor_state(
10099        &r#"
10100        ˇuse some::modified;
10101
10102
10103        fn main() {
10104            println!("hello there");
10105
10106            println!("around the");
10107            println!("world");
10108        }
10109        "#
10110        .unindent(),
10111    );
10112
10113    cx.update_editor(|editor, cx| {
10114        //Wrap around the top of the buffer
10115        for _ in 0..2 {
10116            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10117        }
10118    });
10119
10120    cx.assert_editor_state(
10121        &r#"
10122        use some::modified;
10123
10124
10125        fn main() {
10126        ˇ    println!("hello there");
10127
10128            println!("around the");
10129            println!("world");
10130        }
10131        "#
10132        .unindent(),
10133    );
10134
10135    cx.update_editor(|editor, cx| {
10136        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10137    });
10138
10139    cx.assert_editor_state(
10140        &r#"
10141        use some::modified;
10142
10143        ˇ
10144        fn main() {
10145            println!("hello there");
10146
10147            println!("around the");
10148            println!("world");
10149        }
10150        "#
10151        .unindent(),
10152    );
10153
10154    cx.update_editor(|editor, cx| {
10155        for _ in 0..3 {
10156            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10157        }
10158    });
10159
10160    cx.assert_editor_state(
10161        &r#"
10162        use some::modified;
10163
10164
10165        fn main() {
10166        ˇ    println!("hello there");
10167
10168            println!("around the");
10169            println!("world");
10170        }
10171        "#
10172        .unindent(),
10173    );
10174
10175    cx.update_editor(|editor, cx| {
10176        editor.fold(&Fold, cx);
10177
10178        //Make sure that the fold only gets one hunk
10179        for _ in 0..4 {
10180            editor.go_to_next_hunk(&GoToHunk, cx);
10181        }
10182    });
10183
10184    cx.assert_editor_state(
10185        &r#"
10186        ˇuse some::modified;
10187
10188
10189        fn main() {
10190            println!("hello there");
10191
10192            println!("around the");
10193            println!("world");
10194        }
10195        "#
10196        .unindent(),
10197    );
10198}
10199
10200#[test]
10201fn test_split_words() {
10202    fn split(text: &str) -> Vec<&str> {
10203        split_words(text).collect()
10204    }
10205
10206    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10207    assert_eq!(split("hello_world"), &["hello_", "world"]);
10208    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10209    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10210    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10211    assert_eq!(split("helloworld"), &["helloworld"]);
10212
10213    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10214}
10215
10216#[gpui::test]
10217async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10218    init_test(cx, |_| {});
10219
10220    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10221    let mut assert = |before, after| {
10222        let _state_context = cx.set_state(before);
10223        cx.update_editor(|editor, cx| {
10224            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10225        });
10226        cx.assert_editor_state(after);
10227    };
10228
10229    // Outside bracket jumps to outside of matching bracket
10230    assert("console.logˇ(var);", "console.log(var)ˇ;");
10231    assert("console.log(var)ˇ;", "console.logˇ(var);");
10232
10233    // Inside bracket jumps to inside of matching bracket
10234    assert("console.log(ˇvar);", "console.log(varˇ);");
10235    assert("console.log(varˇ);", "console.log(ˇvar);");
10236
10237    // When outside a bracket and inside, favor jumping to the inside bracket
10238    assert(
10239        "console.log('foo', [1, 2, 3]ˇ);",
10240        "console.log(ˇ'foo', [1, 2, 3]);",
10241    );
10242    assert(
10243        "console.log(ˇ'foo', [1, 2, 3]);",
10244        "console.log('foo', [1, 2, 3]ˇ);",
10245    );
10246
10247    // Bias forward if two options are equally likely
10248    assert(
10249        "let result = curried_fun()ˇ();",
10250        "let result = curried_fun()()ˇ;",
10251    );
10252
10253    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10254    assert(
10255        indoc! {"
10256            function test() {
10257                console.log('test')ˇ
10258            }"},
10259        indoc! {"
10260            function test() {
10261                console.logˇ('test')
10262            }"},
10263    );
10264}
10265
10266#[gpui::test]
10267async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10268    init_test(cx, |_| {});
10269
10270    let fs = FakeFs::new(cx.executor());
10271    fs.insert_tree(
10272        "/a",
10273        json!({
10274            "main.rs": "fn main() { let a = 5; }",
10275            "other.rs": "// Test file",
10276        }),
10277    )
10278    .await;
10279    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10280
10281    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10282    language_registry.add(Arc::new(Language::new(
10283        LanguageConfig {
10284            name: "Rust".into(),
10285            matcher: LanguageMatcher {
10286                path_suffixes: vec!["rs".to_string()],
10287                ..Default::default()
10288            },
10289            brackets: BracketPairConfig {
10290                pairs: vec![BracketPair {
10291                    start: "{".to_string(),
10292                    end: "}".to_string(),
10293                    close: true,
10294                    surround: true,
10295                    newline: true,
10296                }],
10297                disabled_scopes_by_bracket_ix: Vec::new(),
10298            },
10299            ..Default::default()
10300        },
10301        Some(tree_sitter_rust::LANGUAGE.into()),
10302    )));
10303    let mut fake_servers = language_registry.register_fake_lsp(
10304        "Rust",
10305        FakeLspAdapter {
10306            capabilities: lsp::ServerCapabilities {
10307                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10308                    first_trigger_character: "{".to_string(),
10309                    more_trigger_character: None,
10310                }),
10311                ..Default::default()
10312            },
10313            ..Default::default()
10314        },
10315    );
10316
10317    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10318
10319    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10320
10321    let worktree_id = workspace
10322        .update(cx, |workspace, cx| {
10323            workspace.project().update(cx, |project, cx| {
10324                project.worktrees(cx).next().unwrap().read(cx).id()
10325            })
10326        })
10327        .unwrap();
10328
10329    let buffer = project
10330        .update(cx, |project, cx| {
10331            project.open_local_buffer("/a/main.rs", cx)
10332        })
10333        .await
10334        .unwrap();
10335    cx.executor().run_until_parked();
10336    cx.executor().start_waiting();
10337    let fake_server = fake_servers.next().await.unwrap();
10338    let editor_handle = workspace
10339        .update(cx, |workspace, cx| {
10340            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10341        })
10342        .unwrap()
10343        .await
10344        .unwrap()
10345        .downcast::<Editor>()
10346        .unwrap();
10347
10348    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10349        assert_eq!(
10350            params.text_document_position.text_document.uri,
10351            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10352        );
10353        assert_eq!(
10354            params.text_document_position.position,
10355            lsp::Position::new(0, 21),
10356        );
10357
10358        Ok(Some(vec![lsp::TextEdit {
10359            new_text: "]".to_string(),
10360            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10361        }]))
10362    });
10363
10364    editor_handle.update(cx, |editor, cx| {
10365        editor.focus(cx);
10366        editor.change_selections(None, cx, |s| {
10367            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10368        });
10369        editor.handle_input("{", cx);
10370    });
10371
10372    cx.executor().run_until_parked();
10373
10374    buffer.update(cx, |buffer, _| {
10375        assert_eq!(
10376            buffer.text(),
10377            "fn main() { let a = {5}; }",
10378            "No extra braces from on type formatting should appear in the buffer"
10379        )
10380    });
10381}
10382
10383#[gpui::test]
10384async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10385    init_test(cx, |_| {});
10386
10387    let fs = FakeFs::new(cx.executor());
10388    fs.insert_tree(
10389        "/a",
10390        json!({
10391            "main.rs": "fn main() { let a = 5; }",
10392            "other.rs": "// Test file",
10393        }),
10394    )
10395    .await;
10396
10397    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10398
10399    let server_restarts = Arc::new(AtomicUsize::new(0));
10400    let closure_restarts = Arc::clone(&server_restarts);
10401    let language_server_name = "test language server";
10402    let language_name: LanguageName = "Rust".into();
10403
10404    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10405    language_registry.add(Arc::new(Language::new(
10406        LanguageConfig {
10407            name: language_name.clone(),
10408            matcher: LanguageMatcher {
10409                path_suffixes: vec!["rs".to_string()],
10410                ..Default::default()
10411            },
10412            ..Default::default()
10413        },
10414        Some(tree_sitter_rust::LANGUAGE.into()),
10415    )));
10416    let mut fake_servers = language_registry.register_fake_lsp(
10417        "Rust",
10418        FakeLspAdapter {
10419            name: language_server_name,
10420            initialization_options: Some(json!({
10421                "testOptionValue": true
10422            })),
10423            initializer: Some(Box::new(move |fake_server| {
10424                let task_restarts = Arc::clone(&closure_restarts);
10425                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10426                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10427                    futures::future::ready(Ok(()))
10428                });
10429            })),
10430            ..Default::default()
10431        },
10432    );
10433
10434    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10435    let _buffer = project
10436        .update(cx, |project, cx| {
10437            project.open_local_buffer("/a/main.rs", cx)
10438        })
10439        .await
10440        .unwrap();
10441    let _fake_server = fake_servers.next().await.unwrap();
10442    update_test_language_settings(cx, |language_settings| {
10443        language_settings.languages.insert(
10444            language_name.clone(),
10445            LanguageSettingsContent {
10446                tab_size: NonZeroU32::new(8),
10447                ..Default::default()
10448            },
10449        );
10450    });
10451    cx.executor().run_until_parked();
10452    assert_eq!(
10453        server_restarts.load(atomic::Ordering::Acquire),
10454        0,
10455        "Should not restart LSP server on an unrelated change"
10456    );
10457
10458    update_test_project_settings(cx, |project_settings| {
10459        project_settings.lsp.insert(
10460            "Some other server name".into(),
10461            LspSettings {
10462                binary: None,
10463                settings: None,
10464                initialization_options: Some(json!({
10465                    "some other init value": false
10466                })),
10467            },
10468        );
10469    });
10470    cx.executor().run_until_parked();
10471    assert_eq!(
10472        server_restarts.load(atomic::Ordering::Acquire),
10473        0,
10474        "Should not restart LSP server on an unrelated LSP settings change"
10475    );
10476
10477    update_test_project_settings(cx, |project_settings| {
10478        project_settings.lsp.insert(
10479            language_server_name.into(),
10480            LspSettings {
10481                binary: None,
10482                settings: None,
10483                initialization_options: Some(json!({
10484                    "anotherInitValue": false
10485                })),
10486            },
10487        );
10488    });
10489    cx.executor().run_until_parked();
10490    assert_eq!(
10491        server_restarts.load(atomic::Ordering::Acquire),
10492        1,
10493        "Should restart LSP server on a related LSP settings change"
10494    );
10495
10496    update_test_project_settings(cx, |project_settings| {
10497        project_settings.lsp.insert(
10498            language_server_name.into(),
10499            LspSettings {
10500                binary: None,
10501                settings: None,
10502                initialization_options: Some(json!({
10503                    "anotherInitValue": false
10504                })),
10505            },
10506        );
10507    });
10508    cx.executor().run_until_parked();
10509    assert_eq!(
10510        server_restarts.load(atomic::Ordering::Acquire),
10511        1,
10512        "Should not restart LSP server on a related LSP settings change that is the same"
10513    );
10514
10515    update_test_project_settings(cx, |project_settings| {
10516        project_settings.lsp.insert(
10517            language_server_name.into(),
10518            LspSettings {
10519                binary: None,
10520                settings: None,
10521                initialization_options: None,
10522            },
10523        );
10524    });
10525    cx.executor().run_until_parked();
10526    assert_eq!(
10527        server_restarts.load(atomic::Ordering::Acquire),
10528        2,
10529        "Should restart LSP server on another related LSP settings change"
10530    );
10531}
10532
10533#[gpui::test]
10534async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10535    init_test(cx, |_| {});
10536
10537    let mut cx = EditorLspTestContext::new_rust(
10538        lsp::ServerCapabilities {
10539            completion_provider: Some(lsp::CompletionOptions {
10540                trigger_characters: Some(vec![".".to_string()]),
10541                resolve_provider: Some(true),
10542                ..Default::default()
10543            }),
10544            ..Default::default()
10545        },
10546        cx,
10547    )
10548    .await;
10549
10550    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10551    cx.simulate_keystroke(".");
10552    let completion_item = lsp::CompletionItem {
10553        label: "some".into(),
10554        kind: Some(lsp::CompletionItemKind::SNIPPET),
10555        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10556        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10557            kind: lsp::MarkupKind::Markdown,
10558            value: "```rust\nSome(2)\n```".to_string(),
10559        })),
10560        deprecated: Some(false),
10561        sort_text: Some("fffffff2".to_string()),
10562        filter_text: Some("some".to_string()),
10563        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10564        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10565            range: lsp::Range {
10566                start: lsp::Position {
10567                    line: 0,
10568                    character: 22,
10569                },
10570                end: lsp::Position {
10571                    line: 0,
10572                    character: 22,
10573                },
10574            },
10575            new_text: "Some(2)".to_string(),
10576        })),
10577        additional_text_edits: Some(vec![lsp::TextEdit {
10578            range: lsp::Range {
10579                start: lsp::Position {
10580                    line: 0,
10581                    character: 20,
10582                },
10583                end: lsp::Position {
10584                    line: 0,
10585                    character: 22,
10586                },
10587            },
10588            new_text: "".to_string(),
10589        }]),
10590        ..Default::default()
10591    };
10592
10593    let closure_completion_item = completion_item.clone();
10594    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10595        let task_completion_item = closure_completion_item.clone();
10596        async move {
10597            Ok(Some(lsp::CompletionResponse::Array(vec![
10598                task_completion_item,
10599            ])))
10600        }
10601    });
10602
10603    request.next().await;
10604
10605    cx.condition(|editor, _| editor.context_menu_visible())
10606        .await;
10607    let apply_additional_edits = cx.update_editor(|editor, cx| {
10608        editor
10609            .confirm_completion(&ConfirmCompletion::default(), cx)
10610            .unwrap()
10611    });
10612    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10613
10614    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10615        let task_completion_item = completion_item.clone();
10616        async move { Ok(task_completion_item) }
10617    })
10618    .next()
10619    .await
10620    .unwrap();
10621    apply_additional_edits.await.unwrap();
10622    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10623}
10624
10625#[gpui::test]
10626async fn test_completions_resolve_updates_labels(cx: &mut gpui::TestAppContext) {
10627    init_test(cx, |_| {});
10628
10629    let mut cx = EditorLspTestContext::new_rust(
10630        lsp::ServerCapabilities {
10631            completion_provider: Some(lsp::CompletionOptions {
10632                trigger_characters: Some(vec![".".to_string()]),
10633                resolve_provider: Some(true),
10634                ..Default::default()
10635            }),
10636            ..Default::default()
10637        },
10638        cx,
10639    )
10640    .await;
10641
10642    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10643    cx.simulate_keystroke(".");
10644
10645    let completion_item = lsp::CompletionItem {
10646        label: "unresolved".to_string(),
10647        detail: None,
10648        documentation: None,
10649        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10650            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10651            new_text: ".unresolved".to_string(),
10652        })),
10653        ..lsp::CompletionItem::default()
10654    };
10655
10656    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10657        let item = completion_item.clone();
10658        async move { Ok(Some(lsp::CompletionResponse::Array(vec![item]))) }
10659    })
10660    .next()
10661    .await;
10662
10663    cx.condition(|editor, _| editor.context_menu_visible())
10664        .await;
10665    cx.update_editor(|editor, _| {
10666        let context_menu = editor.context_menu.read();
10667        let context_menu = context_menu
10668            .as_ref()
10669            .expect("Should have the context menu deployed");
10670        match context_menu {
10671            CodeContextMenu::Completions(completions_menu) => {
10672                let completions = completions_menu.completions.read();
10673                assert_eq!(completions.len(), 1, "Should have one completion");
10674                assert_eq!(completions.get(0).unwrap().label.text, "unresolved");
10675            }
10676            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10677        }
10678    });
10679
10680    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10681        Ok(lsp::CompletionItem {
10682            label: "resolved".to_string(),
10683            detail: Some("Now resolved!".to_string()),
10684            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10685            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10686                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10687                new_text: ".resolved".to_string(),
10688            })),
10689            ..lsp::CompletionItem::default()
10690        })
10691    })
10692    .next()
10693    .await;
10694    cx.run_until_parked();
10695
10696    cx.update_editor(|editor, _| {
10697        let context_menu = editor.context_menu.read();
10698        let context_menu = context_menu
10699            .as_ref()
10700            .expect("Should have the context menu deployed");
10701        match context_menu {
10702            CodeContextMenu::Completions(completions_menu) => {
10703                let completions = completions_menu.completions.read();
10704                assert_eq!(completions.len(), 1, "Should have one completion");
10705                assert_eq!(
10706                    completions.get(0).unwrap().label.text,
10707                    "resolved",
10708                    "Should update the completion label after resolving"
10709                );
10710            }
10711            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10712        }
10713    });
10714}
10715
10716#[gpui::test]
10717async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10718    init_test(cx, |_| {});
10719
10720    let mut cx = EditorLspTestContext::new_rust(
10721        lsp::ServerCapabilities {
10722            completion_provider: Some(lsp::CompletionOptions {
10723                trigger_characters: Some(vec![".".to_string()]),
10724                resolve_provider: Some(true),
10725                ..Default::default()
10726            }),
10727            ..Default::default()
10728        },
10729        cx,
10730    )
10731    .await;
10732
10733    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10734    cx.simulate_keystroke(".");
10735
10736    let default_commit_characters = vec!["?".to_string()];
10737    let default_data = json!({ "very": "special"});
10738    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10739    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10740    let default_edit_range = lsp::Range {
10741        start: lsp::Position {
10742            line: 0,
10743            character: 5,
10744        },
10745        end: lsp::Position {
10746            line: 0,
10747            character: 5,
10748        },
10749    };
10750
10751    let resolve_requests_number = Arc::new(AtomicUsize::new(0));
10752    let expect_first_item = Arc::new(AtomicBool::new(true));
10753    cx.lsp
10754        .server
10755        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10756            let closure_default_data = default_data.clone();
10757            let closure_resolve_requests_number = resolve_requests_number.clone();
10758            let closure_expect_first_item = expect_first_item.clone();
10759            let closure_default_commit_characters = default_commit_characters.clone();
10760            move |item_to_resolve, _| {
10761                closure_resolve_requests_number.fetch_add(1, atomic::Ordering::Release);
10762                let default_data = closure_default_data.clone();
10763                let default_commit_characters = closure_default_commit_characters.clone();
10764                let expect_first_item = closure_expect_first_item.clone();
10765                async move {
10766                    if expect_first_item.load(atomic::Ordering::Acquire) {
10767                        assert_eq!(
10768                            item_to_resolve.label, "Some(2)",
10769                            "Should have selected the first item"
10770                        );
10771                        assert_eq!(
10772                            item_to_resolve.data,
10773                            Some(json!({ "very": "special"})),
10774                            "First item should bring its own data for resolving"
10775                        );
10776                        assert_eq!(
10777                            item_to_resolve.commit_characters,
10778                            Some(default_commit_characters),
10779                            "First item had no own commit characters and should inherit the default ones"
10780                        );
10781                        assert!(
10782                            matches!(
10783                                item_to_resolve.text_edit,
10784                                Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10785                            ),
10786                            "First item should bring its own edit range for resolving"
10787                        );
10788                        assert_eq!(
10789                            item_to_resolve.insert_text_format,
10790                            Some(default_insert_text_format),
10791                            "First item had no own insert text format and should inherit the default one"
10792                        );
10793                        assert_eq!(
10794                            item_to_resolve.insert_text_mode,
10795                            Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10796                            "First item should bring its own insert text mode for resolving"
10797                        );
10798                        Ok(item_to_resolve)
10799                    } else {
10800                        assert_eq!(
10801                            item_to_resolve.label, "vec![2]",
10802                            "Should have selected the last item"
10803                        );
10804                        assert_eq!(
10805                            item_to_resolve.data,
10806                            Some(default_data),
10807                            "Last item has no own resolve data and should inherit the default one"
10808                        );
10809                        assert_eq!(
10810                            item_to_resolve.commit_characters,
10811                            Some(default_commit_characters),
10812                            "Last item had no own commit characters and should inherit the default ones"
10813                        );
10814                        assert_eq!(
10815                            item_to_resolve.text_edit,
10816                            Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10817                                range: default_edit_range,
10818                                new_text: "vec![2]".to_string()
10819                            })),
10820                            "Last item had no own edit range and should inherit the default one"
10821                        );
10822                        assert_eq!(
10823                            item_to_resolve.insert_text_format,
10824                            Some(lsp::InsertTextFormat::PLAIN_TEXT),
10825                            "Last item should bring its own insert text format for resolving"
10826                        );
10827                        assert_eq!(
10828                            item_to_resolve.insert_text_mode,
10829                            Some(default_insert_text_mode),
10830                            "Last item had no own insert text mode and should inherit the default one"
10831                        );
10832
10833                        Ok(item_to_resolve)
10834                    }
10835                }
10836            }
10837        }).detach();
10838
10839    let completion_data = default_data.clone();
10840    let completion_characters = default_commit_characters.clone();
10841    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10842        let default_data = completion_data.clone();
10843        let default_commit_characters = completion_characters.clone();
10844        async move {
10845            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10846                items: vec![
10847                    lsp::CompletionItem {
10848                        label: "Some(2)".into(),
10849                        insert_text: Some("Some(2)".into()),
10850                        data: Some(json!({ "very": "special"})),
10851                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10852                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10853                            lsp::InsertReplaceEdit {
10854                                new_text: "Some(2)".to_string(),
10855                                insert: lsp::Range::default(),
10856                                replace: lsp::Range::default(),
10857                            },
10858                        )),
10859                        ..lsp::CompletionItem::default()
10860                    },
10861                    lsp::CompletionItem {
10862                        label: "vec![2]".into(),
10863                        insert_text: Some("vec![2]".into()),
10864                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10865                        ..lsp::CompletionItem::default()
10866                    },
10867                ],
10868                item_defaults: Some(lsp::CompletionListItemDefaults {
10869                    data: Some(default_data.clone()),
10870                    commit_characters: Some(default_commit_characters.clone()),
10871                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10872                        default_edit_range,
10873                    )),
10874                    insert_text_format: Some(default_insert_text_format),
10875                    insert_text_mode: Some(default_insert_text_mode),
10876                }),
10877                ..lsp::CompletionList::default()
10878            })))
10879        }
10880    })
10881    .next()
10882    .await;
10883
10884    cx.condition(|editor, _| editor.context_menu_visible())
10885        .await;
10886    cx.run_until_parked();
10887    cx.update_editor(|editor, _| {
10888        let menu = editor.context_menu.read();
10889        match menu.as_ref().expect("should have the completions menu") {
10890            CodeContextMenu::Completions(completions_menu) => {
10891                assert_eq!(
10892                    completions_menu
10893                        .matches
10894                        .iter()
10895                        .map(|c| c.string.as_str())
10896                        .collect::<Vec<_>>(),
10897                    vec!["Some(2)", "vec![2]"]
10898                );
10899            }
10900            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10901        }
10902    });
10903    assert_eq!(
10904        resolve_requests_number.load(atomic::Ordering::Acquire),
10905        1,
10906        "While there are 2 items in the completion list, only 1 resolve request should have been sent, for the selected item"
10907    );
10908
10909    cx.update_editor(|editor, cx| {
10910        editor.context_menu_first(&ContextMenuFirst, cx);
10911    });
10912    cx.run_until_parked();
10913    assert_eq!(
10914        resolve_requests_number.load(atomic::Ordering::Acquire),
10915        2,
10916        "After re-selecting the first item, another resolve request should have been sent"
10917    );
10918
10919    expect_first_item.store(false, atomic::Ordering::Release);
10920    cx.update_editor(|editor, cx| {
10921        editor.context_menu_last(&ContextMenuLast, cx);
10922    });
10923    cx.run_until_parked();
10924    assert_eq!(
10925        resolve_requests_number.load(atomic::Ordering::Acquire),
10926        3,
10927        "After selecting the other item, another resolve request should have been sent"
10928    );
10929}
10930
10931#[gpui::test]
10932async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10933    init_test(cx, |_| {});
10934
10935    let mut cx = EditorLspTestContext::new(
10936        Language::new(
10937            LanguageConfig {
10938                matcher: LanguageMatcher {
10939                    path_suffixes: vec!["jsx".into()],
10940                    ..Default::default()
10941                },
10942                overrides: [(
10943                    "element".into(),
10944                    LanguageConfigOverride {
10945                        word_characters: Override::Set(['-'].into_iter().collect()),
10946                        ..Default::default()
10947                    },
10948                )]
10949                .into_iter()
10950                .collect(),
10951                ..Default::default()
10952            },
10953            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10954        )
10955        .with_override_query("(jsx_self_closing_element) @element")
10956        .unwrap(),
10957        lsp::ServerCapabilities {
10958            completion_provider: Some(lsp::CompletionOptions {
10959                trigger_characters: Some(vec![":".to_string()]),
10960                ..Default::default()
10961            }),
10962            ..Default::default()
10963        },
10964        cx,
10965    )
10966    .await;
10967
10968    cx.lsp
10969        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10970            Ok(Some(lsp::CompletionResponse::Array(vec![
10971                lsp::CompletionItem {
10972                    label: "bg-blue".into(),
10973                    ..Default::default()
10974                },
10975                lsp::CompletionItem {
10976                    label: "bg-red".into(),
10977                    ..Default::default()
10978                },
10979                lsp::CompletionItem {
10980                    label: "bg-yellow".into(),
10981                    ..Default::default()
10982                },
10983            ])))
10984        });
10985
10986    cx.set_state(r#"<p class="bgˇ" />"#);
10987
10988    // Trigger completion when typing a dash, because the dash is an extra
10989    // word character in the 'element' scope, which contains the cursor.
10990    cx.simulate_keystroke("-");
10991    cx.executor().run_until_parked();
10992    cx.update_editor(|editor, _| {
10993        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10994            assert_eq!(
10995                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10996                &["bg-red", "bg-blue", "bg-yellow"]
10997            );
10998        } else {
10999            panic!("expected completion menu to be open");
11000        }
11001    });
11002
11003    cx.simulate_keystroke("l");
11004    cx.executor().run_until_parked();
11005    cx.update_editor(|editor, _| {
11006        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
11007            assert_eq!(
11008                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
11009                &["bg-blue", "bg-yellow"]
11010            );
11011        } else {
11012            panic!("expected completion menu to be open");
11013        }
11014    });
11015
11016    // When filtering completions, consider the character after the '-' to
11017    // be the start of a subword.
11018    cx.set_state(r#"<p class="yelˇ" />"#);
11019    cx.simulate_keystroke("l");
11020    cx.executor().run_until_parked();
11021    cx.update_editor(|editor, _| {
11022        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
11023            assert_eq!(
11024                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
11025                &["bg-yellow"]
11026            );
11027        } else {
11028            panic!("expected completion menu to be open");
11029        }
11030    });
11031}
11032
11033#[gpui::test]
11034async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11035    init_test(cx, |settings| {
11036        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11037            FormatterList(vec![Formatter::Prettier].into()),
11038        ))
11039    });
11040
11041    let fs = FakeFs::new(cx.executor());
11042    fs.insert_file("/file.ts", Default::default()).await;
11043
11044    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11045    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11046
11047    language_registry.add(Arc::new(Language::new(
11048        LanguageConfig {
11049            name: "TypeScript".into(),
11050            matcher: LanguageMatcher {
11051                path_suffixes: vec!["ts".to_string()],
11052                ..Default::default()
11053            },
11054            ..Default::default()
11055        },
11056        Some(tree_sitter_rust::LANGUAGE.into()),
11057    )));
11058    update_test_language_settings(cx, |settings| {
11059        settings.defaults.prettier = Some(PrettierSettings {
11060            allowed: true,
11061            ..PrettierSettings::default()
11062        });
11063    });
11064
11065    let test_plugin = "test_plugin";
11066    let _ = language_registry.register_fake_lsp(
11067        "TypeScript",
11068        FakeLspAdapter {
11069            prettier_plugins: vec![test_plugin],
11070            ..Default::default()
11071        },
11072    );
11073
11074    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11075    let buffer = project
11076        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11077        .await
11078        .unwrap();
11079
11080    let buffer_text = "one\ntwo\nthree\n";
11081    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11082    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11083    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11084
11085    editor
11086        .update(cx, |editor, cx| {
11087            editor.perform_format(
11088                project.clone(),
11089                FormatTrigger::Manual,
11090                FormatTarget::Buffer,
11091                cx,
11092            )
11093        })
11094        .unwrap()
11095        .await;
11096    assert_eq!(
11097        editor.update(cx, |editor, cx| editor.text(cx)),
11098        buffer_text.to_string() + prettier_format_suffix,
11099        "Test prettier formatting was not applied to the original buffer text",
11100    );
11101
11102    update_test_language_settings(cx, |settings| {
11103        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11104    });
11105    let format = editor.update(cx, |editor, cx| {
11106        editor.perform_format(
11107            project.clone(),
11108            FormatTrigger::Manual,
11109            FormatTarget::Buffer,
11110            cx,
11111        )
11112    });
11113    format.await.unwrap();
11114    assert_eq!(
11115        editor.update(cx, |editor, cx| editor.text(cx)),
11116        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11117        "Autoformatting (via test prettier) was not applied to the original buffer text",
11118    );
11119}
11120
11121#[gpui::test]
11122async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11123    init_test(cx, |_| {});
11124    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11125    let base_text = indoc! {r#"
11126        struct Row;
11127        struct Row1;
11128        struct Row2;
11129
11130        struct Row4;
11131        struct Row5;
11132        struct Row6;
11133
11134        struct Row8;
11135        struct Row9;
11136        struct Row10;"#};
11137
11138    // When addition hunks are not adjacent to carets, no hunk revert is performed
11139    assert_hunk_revert(
11140        indoc! {r#"struct Row;
11141                   struct Row1;
11142                   struct Row1.1;
11143                   struct Row1.2;
11144                   struct Row2;ˇ
11145
11146                   struct Row4;
11147                   struct Row5;
11148                   struct Row6;
11149
11150                   struct Row8;
11151                   ˇstruct Row9;
11152                   struct Row9.1;
11153                   struct Row9.2;
11154                   struct Row9.3;
11155                   struct Row10;"#},
11156        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11157        indoc! {r#"struct Row;
11158                   struct Row1;
11159                   struct Row1.1;
11160                   struct Row1.2;
11161                   struct Row2;ˇ
11162
11163                   struct Row4;
11164                   struct Row5;
11165                   struct Row6;
11166
11167                   struct Row8;
11168                   ˇstruct Row9;
11169                   struct Row9.1;
11170                   struct Row9.2;
11171                   struct Row9.3;
11172                   struct Row10;"#},
11173        base_text,
11174        &mut cx,
11175    );
11176    // Same for selections
11177    assert_hunk_revert(
11178        indoc! {r#"struct Row;
11179                   struct Row1;
11180                   struct Row2;
11181                   struct Row2.1;
11182                   struct Row2.2;
11183                   «ˇ
11184                   struct Row4;
11185                   struct» Row5;
11186                   «struct Row6;
11187                   ˇ»
11188                   struct Row9.1;
11189                   struct Row9.2;
11190                   struct Row9.3;
11191                   struct Row8;
11192                   struct Row9;
11193                   struct Row10;"#},
11194        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11195        indoc! {r#"struct Row;
11196                   struct Row1;
11197                   struct Row2;
11198                   struct Row2.1;
11199                   struct Row2.2;
11200                   «ˇ
11201                   struct Row4;
11202                   struct» Row5;
11203                   «struct Row6;
11204                   ˇ»
11205                   struct Row9.1;
11206                   struct Row9.2;
11207                   struct Row9.3;
11208                   struct Row8;
11209                   struct Row9;
11210                   struct Row10;"#},
11211        base_text,
11212        &mut cx,
11213    );
11214
11215    // When carets and selections intersect the addition hunks, those are reverted.
11216    // Adjacent carets got merged.
11217    assert_hunk_revert(
11218        indoc! {r#"struct Row;
11219                   ˇ// something on the top
11220                   struct Row1;
11221                   struct Row2;
11222                   struct Roˇw3.1;
11223                   struct Row2.2;
11224                   struct Row2.3;ˇ
11225
11226                   struct Row4;
11227                   struct ˇRow5.1;
11228                   struct Row5.2;
11229                   struct «Rowˇ»5.3;
11230                   struct Row5;
11231                   struct Row6;
11232                   ˇ
11233                   struct Row9.1;
11234                   struct «Rowˇ»9.2;
11235                   struct «ˇRow»9.3;
11236                   struct Row8;
11237                   struct Row9;
11238                   «ˇ// something on bottom»
11239                   struct Row10;"#},
11240        vec![
11241            DiffHunkStatus::Added,
11242            DiffHunkStatus::Added,
11243            DiffHunkStatus::Added,
11244            DiffHunkStatus::Added,
11245            DiffHunkStatus::Added,
11246        ],
11247        indoc! {r#"struct Row;
11248                   ˇstruct Row1;
11249                   struct Row2;
11250                   ˇ
11251                   struct Row4;
11252                   ˇstruct Row5;
11253                   struct Row6;
11254                   ˇ
11255                   ˇstruct Row8;
11256                   struct Row9;
11257                   ˇstruct Row10;"#},
11258        base_text,
11259        &mut cx,
11260    );
11261}
11262
11263#[gpui::test]
11264async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11265    init_test(cx, |_| {});
11266    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11267    let base_text = indoc! {r#"
11268        struct Row;
11269        struct Row1;
11270        struct Row2;
11271
11272        struct Row4;
11273        struct Row5;
11274        struct Row6;
11275
11276        struct Row8;
11277        struct Row9;
11278        struct Row10;"#};
11279
11280    // Modification hunks behave the same as the addition ones.
11281    assert_hunk_revert(
11282        indoc! {r#"struct Row;
11283                   struct Row1;
11284                   struct Row33;
11285                   ˇ
11286                   struct Row4;
11287                   struct Row5;
11288                   struct Row6;
11289                   ˇ
11290                   struct Row99;
11291                   struct Row9;
11292                   struct Row10;"#},
11293        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11294        indoc! {r#"struct Row;
11295                   struct Row1;
11296                   struct Row33;
11297                   ˇ
11298                   struct Row4;
11299                   struct Row5;
11300                   struct Row6;
11301                   ˇ
11302                   struct Row99;
11303                   struct Row9;
11304                   struct Row10;"#},
11305        base_text,
11306        &mut cx,
11307    );
11308    assert_hunk_revert(
11309        indoc! {r#"struct Row;
11310                   struct Row1;
11311                   struct Row33;
11312                   «ˇ
11313                   struct Row4;
11314                   struct» Row5;
11315                   «struct Row6;
11316                   ˇ»
11317                   struct Row99;
11318                   struct Row9;
11319                   struct Row10;"#},
11320        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11321        indoc! {r#"struct Row;
11322                   struct Row1;
11323                   struct Row33;
11324                   «ˇ
11325                   struct Row4;
11326                   struct» Row5;
11327                   «struct Row6;
11328                   ˇ»
11329                   struct Row99;
11330                   struct Row9;
11331                   struct Row10;"#},
11332        base_text,
11333        &mut cx,
11334    );
11335
11336    assert_hunk_revert(
11337        indoc! {r#"ˇstruct Row1.1;
11338                   struct Row1;
11339                   «ˇstr»uct Row22;
11340
11341                   struct ˇRow44;
11342                   struct Row5;
11343                   struct «Rˇ»ow66;ˇ
11344
11345                   «struˇ»ct Row88;
11346                   struct Row9;
11347                   struct Row1011;ˇ"#},
11348        vec![
11349            DiffHunkStatus::Modified,
11350            DiffHunkStatus::Modified,
11351            DiffHunkStatus::Modified,
11352            DiffHunkStatus::Modified,
11353            DiffHunkStatus::Modified,
11354            DiffHunkStatus::Modified,
11355        ],
11356        indoc! {r#"struct Row;
11357                   ˇstruct Row1;
11358                   struct Row2;
11359                   ˇ
11360                   struct Row4;
11361                   ˇstruct Row5;
11362                   struct Row6;
11363                   ˇ
11364                   struct Row8;
11365                   ˇstruct Row9;
11366                   struct Row10;ˇ"#},
11367        base_text,
11368        &mut cx,
11369    );
11370}
11371
11372#[gpui::test]
11373async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11374    init_test(cx, |_| {});
11375    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11376    let base_text = indoc! {r#"struct Row;
11377struct Row1;
11378struct Row2;
11379
11380struct Row4;
11381struct Row5;
11382struct Row6;
11383
11384struct Row8;
11385struct Row9;
11386struct Row10;"#};
11387
11388    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11389    assert_hunk_revert(
11390        indoc! {r#"struct Row;
11391                   struct Row2;
11392
11393                   ˇstruct Row4;
11394                   struct Row5;
11395                   struct Row6;
11396                   ˇ
11397                   struct Row8;
11398                   struct Row10;"#},
11399        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11400        indoc! {r#"struct Row;
11401                   struct Row2;
11402
11403                   ˇstruct Row4;
11404                   struct Row5;
11405                   struct Row6;
11406                   ˇ
11407                   struct Row8;
11408                   struct Row10;"#},
11409        base_text,
11410        &mut cx,
11411    );
11412    assert_hunk_revert(
11413        indoc! {r#"struct Row;
11414                   struct Row2;
11415
11416                   «ˇstruct Row4;
11417                   struct» Row5;
11418                   «struct Row6;
11419                   ˇ»
11420                   struct Row8;
11421                   struct Row10;"#},
11422        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11423        indoc! {r#"struct Row;
11424                   struct Row2;
11425
11426                   «ˇstruct Row4;
11427                   struct» Row5;
11428                   «struct Row6;
11429                   ˇ»
11430                   struct Row8;
11431                   struct Row10;"#},
11432        base_text,
11433        &mut cx,
11434    );
11435
11436    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11437    assert_hunk_revert(
11438        indoc! {r#"struct Row;
11439                   ˇstruct Row2;
11440
11441                   struct Row4;
11442                   struct Row5;
11443                   struct Row6;
11444
11445                   struct Row8;ˇ
11446                   struct Row10;"#},
11447        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11448        indoc! {r#"struct Row;
11449                   struct Row1;
11450                   ˇstruct Row2;
11451
11452                   struct Row4;
11453                   struct Row5;
11454                   struct Row6;
11455
11456                   struct Row8;ˇ
11457                   struct Row9;
11458                   struct Row10;"#},
11459        base_text,
11460        &mut cx,
11461    );
11462    assert_hunk_revert(
11463        indoc! {r#"struct Row;
11464                   struct Row2«ˇ;
11465                   struct Row4;
11466                   struct» Row5;
11467                   «struct Row6;
11468
11469                   struct Row8;ˇ»
11470                   struct Row10;"#},
11471        vec![
11472            DiffHunkStatus::Removed,
11473            DiffHunkStatus::Removed,
11474            DiffHunkStatus::Removed,
11475        ],
11476        indoc! {r#"struct Row;
11477                   struct Row1;
11478                   struct Row2«ˇ;
11479
11480                   struct Row4;
11481                   struct» Row5;
11482                   «struct Row6;
11483
11484                   struct Row8;ˇ»
11485                   struct Row9;
11486                   struct Row10;"#},
11487        base_text,
11488        &mut cx,
11489    );
11490}
11491
11492#[gpui::test]
11493async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11494    init_test(cx, |_| {});
11495
11496    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11497    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11498    let base_text_3 =
11499        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11500
11501    let text_1 = edit_first_char_of_every_line(base_text_1);
11502    let text_2 = edit_first_char_of_every_line(base_text_2);
11503    let text_3 = edit_first_char_of_every_line(base_text_3);
11504
11505    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11506    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11507    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11508
11509    let multibuffer = cx.new_model(|cx| {
11510        let mut multibuffer = MultiBuffer::new(ReadWrite);
11511        multibuffer.push_excerpts(
11512            buffer_1.clone(),
11513            [
11514                ExcerptRange {
11515                    context: Point::new(0, 0)..Point::new(3, 0),
11516                    primary: None,
11517                },
11518                ExcerptRange {
11519                    context: Point::new(5, 0)..Point::new(7, 0),
11520                    primary: None,
11521                },
11522                ExcerptRange {
11523                    context: Point::new(9, 0)..Point::new(10, 4),
11524                    primary: None,
11525                },
11526            ],
11527            cx,
11528        );
11529        multibuffer.push_excerpts(
11530            buffer_2.clone(),
11531            [
11532                ExcerptRange {
11533                    context: Point::new(0, 0)..Point::new(3, 0),
11534                    primary: None,
11535                },
11536                ExcerptRange {
11537                    context: Point::new(5, 0)..Point::new(7, 0),
11538                    primary: None,
11539                },
11540                ExcerptRange {
11541                    context: Point::new(9, 0)..Point::new(10, 4),
11542                    primary: None,
11543                },
11544            ],
11545            cx,
11546        );
11547        multibuffer.push_excerpts(
11548            buffer_3.clone(),
11549            [
11550                ExcerptRange {
11551                    context: Point::new(0, 0)..Point::new(3, 0),
11552                    primary: None,
11553                },
11554                ExcerptRange {
11555                    context: Point::new(5, 0)..Point::new(7, 0),
11556                    primary: None,
11557                },
11558                ExcerptRange {
11559                    context: Point::new(9, 0)..Point::new(10, 4),
11560                    primary: None,
11561                },
11562            ],
11563            cx,
11564        );
11565        multibuffer
11566    });
11567
11568    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11569    editor.update(cx, |editor, cx| {
11570        for (buffer, diff_base) in [
11571            (buffer_1.clone(), base_text_1),
11572            (buffer_2.clone(), base_text_2),
11573            (buffer_3.clone(), base_text_3),
11574        ] {
11575            let change_set = cx.new_model(|cx| {
11576                BufferChangeSet::new_with_base_text(
11577                    diff_base.to_string(),
11578                    buffer.read(cx).text_snapshot(),
11579                    cx,
11580                )
11581            });
11582            editor.diff_map.add_change_set(change_set, cx)
11583        }
11584    });
11585    cx.executor().run_until_parked();
11586
11587    editor.update(cx, |editor, cx| {
11588        assert_eq!(editor.text(cx), "Xaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
11589        editor.select_all(&SelectAll, cx);
11590        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11591    });
11592    cx.executor().run_until_parked();
11593
11594    // When all ranges are selected, all buffer hunks are reverted.
11595    editor.update(cx, |editor, cx| {
11596        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");
11597    });
11598    buffer_1.update(cx, |buffer, _| {
11599        assert_eq!(buffer.text(), base_text_1);
11600    });
11601    buffer_2.update(cx, |buffer, _| {
11602        assert_eq!(buffer.text(), base_text_2);
11603    });
11604    buffer_3.update(cx, |buffer, _| {
11605        assert_eq!(buffer.text(), base_text_3);
11606    });
11607
11608    editor.update(cx, |editor, cx| {
11609        editor.undo(&Default::default(), cx);
11610    });
11611
11612    editor.update(cx, |editor, cx| {
11613        editor.change_selections(None, cx, |s| {
11614            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11615        });
11616        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11617    });
11618
11619    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11620    // but not affect buffer_2 and its related excerpts.
11621    editor.update(cx, |editor, cx| {
11622        assert_eq!(
11623            editor.text(cx),
11624            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
11625        );
11626    });
11627    buffer_1.update(cx, |buffer, _| {
11628        assert_eq!(buffer.text(), base_text_1);
11629    });
11630    buffer_2.update(cx, |buffer, _| {
11631        assert_eq!(
11632            buffer.text(),
11633            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11634        );
11635    });
11636    buffer_3.update(cx, |buffer, _| {
11637        assert_eq!(
11638            buffer.text(),
11639            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11640        );
11641    });
11642
11643    fn edit_first_char_of_every_line(text: &str) -> String {
11644        text.split('\n')
11645            .map(|line| format!("X{}", &line[1..]))
11646            .collect::<Vec<_>>()
11647            .join("\n")
11648    }
11649}
11650
11651#[gpui::test]
11652async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11653    init_test(cx, |_| {});
11654
11655    let cols = 4;
11656    let rows = 10;
11657    let sample_text_1 = sample_text(rows, cols, 'a');
11658    assert_eq!(
11659        sample_text_1,
11660        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11661    );
11662    let sample_text_2 = sample_text(rows, cols, 'l');
11663    assert_eq!(
11664        sample_text_2,
11665        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11666    );
11667    let sample_text_3 = sample_text(rows, cols, 'v');
11668    assert_eq!(
11669        sample_text_3,
11670        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11671    );
11672
11673    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11674    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11675    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11676
11677    let multi_buffer = cx.new_model(|cx| {
11678        let mut multibuffer = MultiBuffer::new(ReadWrite);
11679        multibuffer.push_excerpts(
11680            buffer_1.clone(),
11681            [
11682                ExcerptRange {
11683                    context: Point::new(0, 0)..Point::new(3, 0),
11684                    primary: None,
11685                },
11686                ExcerptRange {
11687                    context: Point::new(5, 0)..Point::new(7, 0),
11688                    primary: None,
11689                },
11690                ExcerptRange {
11691                    context: Point::new(9, 0)..Point::new(10, 4),
11692                    primary: None,
11693                },
11694            ],
11695            cx,
11696        );
11697        multibuffer.push_excerpts(
11698            buffer_2.clone(),
11699            [
11700                ExcerptRange {
11701                    context: Point::new(0, 0)..Point::new(3, 0),
11702                    primary: None,
11703                },
11704                ExcerptRange {
11705                    context: Point::new(5, 0)..Point::new(7, 0),
11706                    primary: None,
11707                },
11708                ExcerptRange {
11709                    context: Point::new(9, 0)..Point::new(10, 4),
11710                    primary: None,
11711                },
11712            ],
11713            cx,
11714        );
11715        multibuffer.push_excerpts(
11716            buffer_3.clone(),
11717            [
11718                ExcerptRange {
11719                    context: Point::new(0, 0)..Point::new(3, 0),
11720                    primary: None,
11721                },
11722                ExcerptRange {
11723                    context: Point::new(5, 0)..Point::new(7, 0),
11724                    primary: None,
11725                },
11726                ExcerptRange {
11727                    context: Point::new(9, 0)..Point::new(10, 4),
11728                    primary: None,
11729                },
11730            ],
11731            cx,
11732        );
11733        multibuffer
11734    });
11735
11736    let fs = FakeFs::new(cx.executor());
11737    fs.insert_tree(
11738        "/a",
11739        json!({
11740            "main.rs": sample_text_1,
11741            "other.rs": sample_text_2,
11742            "lib.rs": sample_text_3,
11743        }),
11744    )
11745    .await;
11746    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11747    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11748    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11749    let multi_buffer_editor = cx.new_view(|cx| {
11750        Editor::new(
11751            EditorMode::Full,
11752            multi_buffer,
11753            Some(project.clone()),
11754            true,
11755            cx,
11756        )
11757    });
11758    let multibuffer_item_id = workspace
11759        .update(cx, |workspace, cx| {
11760            assert!(
11761                workspace.active_item(cx).is_none(),
11762                "active item should be None before the first item is added"
11763            );
11764            workspace.add_item_to_active_pane(
11765                Box::new(multi_buffer_editor.clone()),
11766                None,
11767                true,
11768                cx,
11769            );
11770            let active_item = workspace
11771                .active_item(cx)
11772                .expect("should have an active item after adding the multi buffer");
11773            assert!(
11774                !active_item.is_singleton(cx),
11775                "A multi buffer was expected to active after adding"
11776            );
11777            active_item.item_id()
11778        })
11779        .unwrap();
11780    cx.executor().run_until_parked();
11781
11782    multi_buffer_editor.update(cx, |editor, cx| {
11783        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11784        editor.open_excerpts(&OpenExcerpts, cx);
11785    });
11786    cx.executor().run_until_parked();
11787    let first_item_id = workspace
11788        .update(cx, |workspace, cx| {
11789            let active_item = workspace
11790                .active_item(cx)
11791                .expect("should have an active item after navigating into the 1st buffer");
11792            let first_item_id = active_item.item_id();
11793            assert_ne!(
11794                first_item_id, multibuffer_item_id,
11795                "Should navigate into the 1st buffer and activate it"
11796            );
11797            assert!(
11798                active_item.is_singleton(cx),
11799                "New active item should be a singleton buffer"
11800            );
11801            assert_eq!(
11802                active_item
11803                    .act_as::<Editor>(cx)
11804                    .expect("should have navigated into an editor for the 1st buffer")
11805                    .read(cx)
11806                    .text(cx),
11807                sample_text_1
11808            );
11809
11810            workspace
11811                .go_back(workspace.active_pane().downgrade(), cx)
11812                .detach_and_log_err(cx);
11813
11814            first_item_id
11815        })
11816        .unwrap();
11817    cx.executor().run_until_parked();
11818    workspace
11819        .update(cx, |workspace, cx| {
11820            let active_item = workspace
11821                .active_item(cx)
11822                .expect("should have an active item after navigating back");
11823            assert_eq!(
11824                active_item.item_id(),
11825                multibuffer_item_id,
11826                "Should navigate back to the multi buffer"
11827            );
11828            assert!(!active_item.is_singleton(cx));
11829        })
11830        .unwrap();
11831
11832    multi_buffer_editor.update(cx, |editor, cx| {
11833        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11834            s.select_ranges(Some(39..40))
11835        });
11836        editor.open_excerpts(&OpenExcerpts, cx);
11837    });
11838    cx.executor().run_until_parked();
11839    let second_item_id = workspace
11840        .update(cx, |workspace, cx| {
11841            let active_item = workspace
11842                .active_item(cx)
11843                .expect("should have an active item after navigating into the 2nd buffer");
11844            let second_item_id = active_item.item_id();
11845            assert_ne!(
11846                second_item_id, multibuffer_item_id,
11847                "Should navigate away from the multibuffer"
11848            );
11849            assert_ne!(
11850                second_item_id, first_item_id,
11851                "Should navigate into the 2nd buffer and activate it"
11852            );
11853            assert!(
11854                active_item.is_singleton(cx),
11855                "New active item should be a singleton buffer"
11856            );
11857            assert_eq!(
11858                active_item
11859                    .act_as::<Editor>(cx)
11860                    .expect("should have navigated into an editor")
11861                    .read(cx)
11862                    .text(cx),
11863                sample_text_2
11864            );
11865
11866            workspace
11867                .go_back(workspace.active_pane().downgrade(), cx)
11868                .detach_and_log_err(cx);
11869
11870            second_item_id
11871        })
11872        .unwrap();
11873    cx.executor().run_until_parked();
11874    workspace
11875        .update(cx, |workspace, cx| {
11876            let active_item = workspace
11877                .active_item(cx)
11878                .expect("should have an active item after navigating back from the 2nd buffer");
11879            assert_eq!(
11880                active_item.item_id(),
11881                multibuffer_item_id,
11882                "Should navigate back from the 2nd buffer to the multi buffer"
11883            );
11884            assert!(!active_item.is_singleton(cx));
11885        })
11886        .unwrap();
11887
11888    multi_buffer_editor.update(cx, |editor, cx| {
11889        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11890            s.select_ranges(Some(70..70))
11891        });
11892        editor.open_excerpts(&OpenExcerpts, cx);
11893    });
11894    cx.executor().run_until_parked();
11895    workspace
11896        .update(cx, |workspace, cx| {
11897            let active_item = workspace
11898                .active_item(cx)
11899                .expect("should have an active item after navigating into the 3rd buffer");
11900            let third_item_id = active_item.item_id();
11901            assert_ne!(
11902                third_item_id, multibuffer_item_id,
11903                "Should navigate into the 3rd buffer and activate it"
11904            );
11905            assert_ne!(third_item_id, first_item_id);
11906            assert_ne!(third_item_id, second_item_id);
11907            assert!(
11908                active_item.is_singleton(cx),
11909                "New active item should be a singleton buffer"
11910            );
11911            assert_eq!(
11912                active_item
11913                    .act_as::<Editor>(cx)
11914                    .expect("should have navigated into an editor")
11915                    .read(cx)
11916                    .text(cx),
11917                sample_text_3
11918            );
11919
11920            workspace
11921                .go_back(workspace.active_pane().downgrade(), cx)
11922                .detach_and_log_err(cx);
11923        })
11924        .unwrap();
11925    cx.executor().run_until_parked();
11926    workspace
11927        .update(cx, |workspace, cx| {
11928            let active_item = workspace
11929                .active_item(cx)
11930                .expect("should have an active item after navigating back from the 3rd buffer");
11931            assert_eq!(
11932                active_item.item_id(),
11933                multibuffer_item_id,
11934                "Should navigate back from the 3rd buffer to the multi buffer"
11935            );
11936            assert!(!active_item.is_singleton(cx));
11937        })
11938        .unwrap();
11939}
11940
11941#[gpui::test]
11942async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11943    init_test(cx, |_| {});
11944
11945    let mut cx = EditorTestContext::new(cx).await;
11946
11947    let diff_base = r#"
11948        use some::mod;
11949
11950        const A: u32 = 42;
11951
11952        fn main() {
11953            println!("hello");
11954
11955            println!("world");
11956        }
11957        "#
11958    .unindent();
11959
11960    cx.set_state(
11961        &r#"
11962        use some::modified;
11963
11964        ˇ
11965        fn main() {
11966            println!("hello there");
11967
11968            println!("around the");
11969            println!("world");
11970        }
11971        "#
11972        .unindent(),
11973    );
11974
11975    cx.set_diff_base(&diff_base);
11976    executor.run_until_parked();
11977
11978    cx.update_editor(|editor, cx| {
11979        editor.go_to_next_hunk(&GoToHunk, cx);
11980        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11981    });
11982    executor.run_until_parked();
11983    cx.assert_state_with_diff(
11984        r#"
11985          use some::modified;
11986
11987
11988          fn main() {
11989        -     println!("hello");
11990        + ˇ    println!("hello there");
11991
11992              println!("around the");
11993              println!("world");
11994          }
11995        "#
11996        .unindent(),
11997    );
11998
11999    cx.update_editor(|editor, cx| {
12000        for _ in 0..3 {
12001            editor.go_to_next_hunk(&GoToHunk, cx);
12002            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12003        }
12004    });
12005    executor.run_until_parked();
12006    cx.assert_state_with_diff(
12007        r#"
12008        - use some::mod;
12009        + use some::modified;
12010
12011        - const A: u32 = 42;
12012          ˇ
12013          fn main() {
12014        -     println!("hello");
12015        +     println!("hello there");
12016
12017        +     println!("around the");
12018              println!("world");
12019          }
12020        "#
12021        .unindent(),
12022    );
12023
12024    cx.update_editor(|editor, cx| {
12025        editor.cancel(&Cancel, cx);
12026    });
12027
12028    cx.assert_state_with_diff(
12029        r#"
12030          use some::modified;
12031
12032          ˇ
12033          fn main() {
12034              println!("hello there");
12035
12036              println!("around the");
12037              println!("world");
12038          }
12039        "#
12040        .unindent(),
12041    );
12042}
12043
12044#[gpui::test]
12045async fn test_diff_base_change_with_expanded_diff_hunks(
12046    executor: BackgroundExecutor,
12047    cx: &mut gpui::TestAppContext,
12048) {
12049    init_test(cx, |_| {});
12050
12051    let mut cx = EditorTestContext::new(cx).await;
12052
12053    let diff_base = r#"
12054        use some::mod1;
12055        use some::mod2;
12056
12057        const A: u32 = 42;
12058        const B: u32 = 42;
12059        const C: u32 = 42;
12060
12061        fn main() {
12062            println!("hello");
12063
12064            println!("world");
12065        }
12066        "#
12067    .unindent();
12068
12069    cx.set_state(
12070        &r#"
12071        use some::mod2;
12072
12073        const A: u32 = 42;
12074        const C: u32 = 42;
12075
12076        fn main(ˇ) {
12077            //println!("hello");
12078
12079            println!("world");
12080            //
12081            //
12082        }
12083        "#
12084        .unindent(),
12085    );
12086
12087    cx.set_diff_base(&diff_base);
12088    executor.run_until_parked();
12089
12090    cx.update_editor(|editor, cx| {
12091        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12092    });
12093    executor.run_until_parked();
12094    cx.assert_state_with_diff(
12095        r#"
12096        - use some::mod1;
12097          use some::mod2;
12098
12099          const A: u32 = 42;
12100        - const B: u32 = 42;
12101          const C: u32 = 42;
12102
12103          fn main(ˇ) {
12104        -     println!("hello");
12105        +     //println!("hello");
12106
12107              println!("world");
12108        +     //
12109        +     //
12110          }
12111        "#
12112        .unindent(),
12113    );
12114
12115    cx.set_diff_base("new diff base!");
12116    executor.run_until_parked();
12117    cx.assert_state_with_diff(
12118        r#"
12119          use some::mod2;
12120
12121          const A: u32 = 42;
12122          const C: u32 = 42;
12123
12124          fn main(ˇ) {
12125              //println!("hello");
12126
12127              println!("world");
12128              //
12129              //
12130          }
12131        "#
12132        .unindent(),
12133    );
12134
12135    cx.update_editor(|editor, cx| {
12136        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12137    });
12138    executor.run_until_parked();
12139    cx.assert_state_with_diff(
12140        r#"
12141        - new diff base!
12142        + use some::mod2;
12143        +
12144        + const A: u32 = 42;
12145        + const C: u32 = 42;
12146        +
12147        + fn main(ˇ) {
12148        +     //println!("hello");
12149        +
12150        +     println!("world");
12151        +     //
12152        +     //
12153        + }
12154        "#
12155        .unindent(),
12156    );
12157}
12158
12159#[gpui::test]
12160async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12161    init_test(cx, |_| {});
12162
12163    let mut cx = EditorTestContext::new(cx).await;
12164
12165    let diff_base = r#"
12166        use some::mod1;
12167        use some::mod2;
12168
12169        const A: u32 = 42;
12170        const B: u32 = 42;
12171        const C: u32 = 42;
12172
12173        fn main() {
12174            println!("hello");
12175
12176            println!("world");
12177        }
12178
12179        fn another() {
12180            println!("another");
12181        }
12182
12183        fn another2() {
12184            println!("another2");
12185        }
12186        "#
12187    .unindent();
12188
12189    cx.set_state(
12190        &r#"
12191        «use some::mod2;
12192
12193        const A: u32 = 42;
12194        const C: u32 = 42;
12195
12196        fn main() {
12197            //println!("hello");
12198
12199            println!("world");
12200            //
12201            //ˇ»
12202        }
12203
12204        fn another() {
12205            println!("another");
12206            println!("another");
12207        }
12208
12209            println!("another2");
12210        }
12211        "#
12212        .unindent(),
12213    );
12214
12215    cx.set_diff_base(&diff_base);
12216    executor.run_until_parked();
12217
12218    cx.update_editor(|editor, cx| {
12219        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12220    });
12221    executor.run_until_parked();
12222
12223    cx.assert_state_with_diff(
12224        r#"
12225        - use some::mod1;
12226          «use some::mod2;
12227
12228          const A: u32 = 42;
12229        - const B: u32 = 42;
12230          const C: u32 = 42;
12231
12232          fn main() {
12233        -     println!("hello");
12234        +     //println!("hello");
12235
12236              println!("world");
12237        +     //
12238        +     //ˇ»
12239          }
12240
12241          fn another() {
12242              println!("another");
12243        +     println!("another");
12244          }
12245
12246        - fn another2() {
12247              println!("another2");
12248          }
12249        "#
12250        .unindent(),
12251    );
12252
12253    // Fold across some of the diff hunks. They should no longer appear expanded.
12254    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12255    cx.executor().run_until_parked();
12256
12257    // Hunks are not shown if their position is within a fold
12258    cx.assert_state_with_diff(
12259        r#"
12260          «use some::mod2;
12261
12262          const A: u32 = 42;
12263          const C: u32 = 42;
12264
12265          fn main() {
12266              //println!("hello");
12267
12268              println!("world");
12269              //
12270              //ˇ»
12271          }
12272
12273          fn another() {
12274              println!("another");
12275        +     println!("another");
12276          }
12277
12278        - fn another2() {
12279              println!("another2");
12280          }
12281        "#
12282        .unindent(),
12283    );
12284
12285    cx.update_editor(|editor, cx| {
12286        editor.select_all(&SelectAll, cx);
12287        editor.unfold_lines(&UnfoldLines, cx);
12288    });
12289    cx.executor().run_until_parked();
12290
12291    // The deletions reappear when unfolding.
12292    cx.assert_state_with_diff(
12293        r#"
12294        - use some::mod1;
12295          «use some::mod2;
12296
12297          const A: u32 = 42;
12298        - const B: u32 = 42;
12299          const C: u32 = 42;
12300
12301          fn main() {
12302        -     println!("hello");
12303        +     //println!("hello");
12304
12305              println!("world");
12306        +     //
12307        +     //
12308          }
12309
12310          fn another() {
12311              println!("another");
12312        +     println!("another");
12313          }
12314
12315        - fn another2() {
12316              println!("another2");
12317          }
12318          ˇ»"#
12319        .unindent(),
12320    );
12321}
12322
12323#[gpui::test]
12324async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12325    init_test(cx, |_| {});
12326
12327    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12328    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12329    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12330    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12331    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12332    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12333
12334    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12335    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12336    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12337
12338    let multi_buffer = cx.new_model(|cx| {
12339        let mut multibuffer = MultiBuffer::new(ReadWrite);
12340        multibuffer.push_excerpts(
12341            buffer_1.clone(),
12342            [
12343                ExcerptRange {
12344                    context: Point::new(0, 0)..Point::new(3, 0),
12345                    primary: None,
12346                },
12347                ExcerptRange {
12348                    context: Point::new(5, 0)..Point::new(7, 0),
12349                    primary: None,
12350                },
12351                ExcerptRange {
12352                    context: Point::new(9, 0)..Point::new(10, 3),
12353                    primary: None,
12354                },
12355            ],
12356            cx,
12357        );
12358        multibuffer.push_excerpts(
12359            buffer_2.clone(),
12360            [
12361                ExcerptRange {
12362                    context: Point::new(0, 0)..Point::new(3, 0),
12363                    primary: None,
12364                },
12365                ExcerptRange {
12366                    context: Point::new(5, 0)..Point::new(7, 0),
12367                    primary: None,
12368                },
12369                ExcerptRange {
12370                    context: Point::new(9, 0)..Point::new(10, 3),
12371                    primary: None,
12372                },
12373            ],
12374            cx,
12375        );
12376        multibuffer.push_excerpts(
12377            buffer_3.clone(),
12378            [
12379                ExcerptRange {
12380                    context: Point::new(0, 0)..Point::new(3, 0),
12381                    primary: None,
12382                },
12383                ExcerptRange {
12384                    context: Point::new(5, 0)..Point::new(7, 0),
12385                    primary: None,
12386                },
12387                ExcerptRange {
12388                    context: Point::new(9, 0)..Point::new(10, 3),
12389                    primary: None,
12390                },
12391            ],
12392            cx,
12393        );
12394        multibuffer
12395    });
12396
12397    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12398    editor
12399        .update(cx, |editor, cx| {
12400            for (buffer, diff_base) in [
12401                (buffer_1.clone(), file_1_old),
12402                (buffer_2.clone(), file_2_old),
12403                (buffer_3.clone(), file_3_old),
12404            ] {
12405                let change_set = cx.new_model(|cx| {
12406                    BufferChangeSet::new_with_base_text(
12407                        diff_base.to_string(),
12408                        buffer.read(cx).text_snapshot(),
12409                        cx,
12410                    )
12411                });
12412                editor.diff_map.add_change_set(change_set, cx)
12413            }
12414        })
12415        .unwrap();
12416
12417    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12418    cx.run_until_parked();
12419
12420    cx.assert_editor_state(
12421        &"
12422            ˇaaa
12423            ccc
12424            ddd
12425
12426            ggg
12427            hhh
12428
12429
12430            lll
12431            mmm
12432            NNN
12433
12434            qqq
12435            rrr
12436
12437            uuu
12438            111
12439            222
12440            333
12441
12442            666
12443            777
12444
12445            000
12446            !!!"
12447        .unindent(),
12448    );
12449
12450    cx.update_editor(|editor, cx| {
12451        editor.select_all(&SelectAll, cx);
12452        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12453    });
12454    cx.executor().run_until_parked();
12455
12456    cx.assert_state_with_diff(
12457        "
12458            «aaa
12459          - bbb
12460            ccc
12461            ddd
12462
12463            ggg
12464            hhh
12465
12466
12467            lll
12468            mmm
12469          - nnn
12470          + NNN
12471
12472            qqq
12473            rrr
12474
12475            uuu
12476            111
12477            222
12478            333
12479
12480          + 666
12481            777
12482
12483            000
12484            !!!ˇ»"
12485            .unindent(),
12486    );
12487}
12488
12489#[gpui::test]
12490async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12491    init_test(cx, |_| {});
12492
12493    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12494    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12495
12496    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12497    let multi_buffer = cx.new_model(|cx| {
12498        let mut multibuffer = MultiBuffer::new(ReadWrite);
12499        multibuffer.push_excerpts(
12500            buffer.clone(),
12501            [
12502                ExcerptRange {
12503                    context: Point::new(0, 0)..Point::new(2, 0),
12504                    primary: None,
12505                },
12506                ExcerptRange {
12507                    context: Point::new(5, 0)..Point::new(7, 0),
12508                    primary: None,
12509                },
12510            ],
12511            cx,
12512        );
12513        multibuffer
12514    });
12515
12516    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12517    editor
12518        .update(cx, |editor, cx| {
12519            let buffer = buffer.read(cx).text_snapshot();
12520            let change_set = cx
12521                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12522            editor.diff_map.add_change_set(change_set, cx)
12523        })
12524        .unwrap();
12525
12526    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12527    cx.run_until_parked();
12528
12529    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12530    cx.executor().run_until_parked();
12531
12532    cx.assert_state_with_diff(
12533        "
12534            ˇaaa
12535          - bbb
12536          + BBB
12537
12538          - ddd
12539          - eee
12540          + EEE
12541            fff
12542        "
12543        .unindent(),
12544    );
12545}
12546
12547#[gpui::test]
12548async fn test_edits_around_expanded_insertion_hunks(
12549    executor: BackgroundExecutor,
12550    cx: &mut gpui::TestAppContext,
12551) {
12552    init_test(cx, |_| {});
12553
12554    let mut cx = EditorTestContext::new(cx).await;
12555
12556    let diff_base = r#"
12557        use some::mod1;
12558        use some::mod2;
12559
12560        const A: u32 = 42;
12561
12562        fn main() {
12563            println!("hello");
12564
12565            println!("world");
12566        }
12567        "#
12568    .unindent();
12569    executor.run_until_parked();
12570    cx.set_state(
12571        &r#"
12572        use some::mod1;
12573        use some::mod2;
12574
12575        const A: u32 = 42;
12576        const B: u32 = 42;
12577        const C: u32 = 42;
12578        ˇ
12579
12580        fn main() {
12581            println!("hello");
12582
12583            println!("world");
12584        }
12585        "#
12586        .unindent(),
12587    );
12588
12589    cx.set_diff_base(&diff_base);
12590    executor.run_until_parked();
12591
12592    cx.update_editor(|editor, cx| {
12593        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12594    });
12595    executor.run_until_parked();
12596
12597    cx.assert_state_with_diff(
12598        r#"
12599        use some::mod1;
12600        use some::mod2;
12601
12602        const A: u32 = 42;
12603      + const B: u32 = 42;
12604      + const C: u32 = 42;
12605      + ˇ
12606
12607        fn main() {
12608            println!("hello");
12609
12610            println!("world");
12611        }
12612        "#
12613        .unindent(),
12614    );
12615
12616    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12617    executor.run_until_parked();
12618
12619    cx.assert_state_with_diff(
12620        r#"
12621        use some::mod1;
12622        use some::mod2;
12623
12624        const A: u32 = 42;
12625      + const B: u32 = 42;
12626      + const C: u32 = 42;
12627      + const D: u32 = 42;
12628      + ˇ
12629
12630        fn main() {
12631            println!("hello");
12632
12633            println!("world");
12634        }
12635        "#
12636        .unindent(),
12637    );
12638
12639    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12640    executor.run_until_parked();
12641
12642    cx.assert_state_with_diff(
12643        r#"
12644        use some::mod1;
12645        use some::mod2;
12646
12647        const A: u32 = 42;
12648      + const B: u32 = 42;
12649      + const C: u32 = 42;
12650      + const D: u32 = 42;
12651      + const E: u32 = 42;
12652      + ˇ
12653
12654        fn main() {
12655            println!("hello");
12656
12657            println!("world");
12658        }
12659        "#
12660        .unindent(),
12661    );
12662
12663    cx.update_editor(|editor, cx| {
12664        editor.delete_line(&DeleteLine, cx);
12665    });
12666    executor.run_until_parked();
12667
12668    cx.assert_state_with_diff(
12669        r#"
12670        use some::mod1;
12671        use some::mod2;
12672
12673        const A: u32 = 42;
12674      + const B: u32 = 42;
12675      + const C: u32 = 42;
12676      + const D: u32 = 42;
12677      + const E: u32 = 42;
12678        ˇ
12679        fn main() {
12680            println!("hello");
12681
12682            println!("world");
12683        }
12684        "#
12685        .unindent(),
12686    );
12687
12688    cx.update_editor(|editor, cx| {
12689        editor.move_up(&MoveUp, cx);
12690        editor.delete_line(&DeleteLine, cx);
12691        editor.move_up(&MoveUp, cx);
12692        editor.delete_line(&DeleteLine, cx);
12693        editor.move_up(&MoveUp, cx);
12694        editor.delete_line(&DeleteLine, cx);
12695    });
12696    executor.run_until_parked();
12697    cx.assert_state_with_diff(
12698        r#"
12699        use some::mod1;
12700        use some::mod2;
12701
12702        const A: u32 = 42;
12703      + const B: u32 = 42;
12704        ˇ
12705        fn main() {
12706            println!("hello");
12707
12708            println!("world");
12709        }
12710        "#
12711        .unindent(),
12712    );
12713
12714    cx.update_editor(|editor, cx| {
12715        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12716        editor.delete_line(&DeleteLine, cx);
12717    });
12718    executor.run_until_parked();
12719    cx.assert_state_with_diff(
12720        r#"
12721        use some::mod1;
12722      - use some::mod2;
12723      -
12724      - const A: u32 = 42;
12725        ˇ
12726        fn main() {
12727            println!("hello");
12728
12729            println!("world");
12730        }
12731        "#
12732        .unindent(),
12733    );
12734}
12735
12736#[gpui::test]
12737async fn test_edits_around_expanded_deletion_hunks(
12738    executor: BackgroundExecutor,
12739    cx: &mut gpui::TestAppContext,
12740) {
12741    init_test(cx, |_| {});
12742
12743    let mut cx = EditorTestContext::new(cx).await;
12744
12745    let diff_base = r#"
12746        use some::mod1;
12747        use some::mod2;
12748
12749        const A: u32 = 42;
12750        const B: u32 = 42;
12751        const C: u32 = 42;
12752
12753
12754        fn main() {
12755            println!("hello");
12756
12757            println!("world");
12758        }
12759    "#
12760    .unindent();
12761    executor.run_until_parked();
12762    cx.set_state(
12763        &r#"
12764        use some::mod1;
12765        use some::mod2;
12766
12767        ˇconst B: u32 = 42;
12768        const C: u32 = 42;
12769
12770
12771        fn main() {
12772            println!("hello");
12773
12774            println!("world");
12775        }
12776        "#
12777        .unindent(),
12778    );
12779
12780    cx.set_diff_base(&diff_base);
12781    executor.run_until_parked();
12782
12783    cx.update_editor(|editor, cx| {
12784        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12785    });
12786    executor.run_until_parked();
12787
12788    cx.assert_state_with_diff(
12789        r#"
12790        use some::mod1;
12791        use some::mod2;
12792
12793      - const A: u32 = 42;
12794        ˇconst B: u32 = 42;
12795        const C: u32 = 42;
12796
12797
12798        fn main() {
12799            println!("hello");
12800
12801            println!("world");
12802        }
12803        "#
12804        .unindent(),
12805    );
12806
12807    cx.update_editor(|editor, cx| {
12808        editor.delete_line(&DeleteLine, cx);
12809    });
12810    executor.run_until_parked();
12811    cx.assert_state_with_diff(
12812        r#"
12813        use some::mod1;
12814        use some::mod2;
12815
12816      - const A: u32 = 42;
12817      - const B: u32 = 42;
12818        ˇconst C: u32 = 42;
12819
12820
12821        fn main() {
12822            println!("hello");
12823
12824            println!("world");
12825        }
12826        "#
12827        .unindent(),
12828    );
12829
12830    cx.update_editor(|editor, cx| {
12831        editor.delete_line(&DeleteLine, cx);
12832    });
12833    executor.run_until_parked();
12834    cx.assert_state_with_diff(
12835        r#"
12836        use some::mod1;
12837        use some::mod2;
12838
12839      - const A: u32 = 42;
12840      - const B: u32 = 42;
12841      - const C: u32 = 42;
12842        ˇ
12843
12844        fn main() {
12845            println!("hello");
12846
12847            println!("world");
12848        }
12849        "#
12850        .unindent(),
12851    );
12852
12853    cx.update_editor(|editor, cx| {
12854        editor.handle_input("replacement", cx);
12855    });
12856    executor.run_until_parked();
12857    cx.assert_state_with_diff(
12858        r#"
12859        use some::mod1;
12860        use some::mod2;
12861
12862      - const A: u32 = 42;
12863      - const B: u32 = 42;
12864      - const C: u32 = 42;
12865      -
12866      + replacementˇ
12867
12868        fn main() {
12869            println!("hello");
12870
12871            println!("world");
12872        }
12873        "#
12874        .unindent(),
12875    );
12876}
12877
12878#[gpui::test]
12879async fn test_edit_after_expanded_modification_hunk(
12880    executor: BackgroundExecutor,
12881    cx: &mut gpui::TestAppContext,
12882) {
12883    init_test(cx, |_| {});
12884
12885    let mut cx = EditorTestContext::new(cx).await;
12886
12887    let diff_base = r#"
12888        use some::mod1;
12889        use some::mod2;
12890
12891        const A: u32 = 42;
12892        const B: u32 = 42;
12893        const C: u32 = 42;
12894        const D: u32 = 42;
12895
12896
12897        fn main() {
12898            println!("hello");
12899
12900            println!("world");
12901        }"#
12902    .unindent();
12903
12904    cx.set_state(
12905        &r#"
12906        use some::mod1;
12907        use some::mod2;
12908
12909        const A: u32 = 42;
12910        const B: u32 = 42;
12911        const C: u32 = 43ˇ
12912        const D: u32 = 42;
12913
12914
12915        fn main() {
12916            println!("hello");
12917
12918            println!("world");
12919        }"#
12920        .unindent(),
12921    );
12922
12923    cx.set_diff_base(&diff_base);
12924    executor.run_until_parked();
12925    cx.update_editor(|editor, cx| {
12926        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12927    });
12928    executor.run_until_parked();
12929
12930    cx.assert_state_with_diff(
12931        r#"
12932        use some::mod1;
12933        use some::mod2;
12934
12935        const A: u32 = 42;
12936        const B: u32 = 42;
12937      - const C: u32 = 42;
12938      + const C: u32 = 43ˇ
12939        const D: u32 = 42;
12940
12941
12942        fn main() {
12943            println!("hello");
12944
12945            println!("world");
12946        }"#
12947        .unindent(),
12948    );
12949
12950    cx.update_editor(|editor, cx| {
12951        editor.handle_input("\nnew_line\n", cx);
12952    });
12953    executor.run_until_parked();
12954
12955    cx.assert_state_with_diff(
12956        r#"
12957        use some::mod1;
12958        use some::mod2;
12959
12960        const A: u32 = 42;
12961        const B: u32 = 42;
12962      - const C: u32 = 42;
12963      + const C: u32 = 43
12964      + new_line
12965      + ˇ
12966        const D: u32 = 42;
12967
12968
12969        fn main() {
12970            println!("hello");
12971
12972            println!("world");
12973        }"#
12974        .unindent(),
12975    );
12976}
12977
12978async fn setup_indent_guides_editor(
12979    text: &str,
12980    cx: &mut gpui::TestAppContext,
12981) -> (BufferId, EditorTestContext) {
12982    init_test(cx, |_| {});
12983
12984    let mut cx = EditorTestContext::new(cx).await;
12985
12986    let buffer_id = cx.update_editor(|editor, cx| {
12987        editor.set_text(text, cx);
12988        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12989
12990        buffer_ids[0]
12991    });
12992
12993    (buffer_id, cx)
12994}
12995
12996fn assert_indent_guides(
12997    range: Range<u32>,
12998    expected: Vec<IndentGuide>,
12999    active_indices: Option<Vec<usize>>,
13000    cx: &mut EditorTestContext,
13001) {
13002    let indent_guides = cx.update_editor(|editor, cx| {
13003        let snapshot = editor.snapshot(cx).display_snapshot;
13004        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13005            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13006            true,
13007            &snapshot,
13008            cx,
13009        );
13010
13011        indent_guides.sort_by(|a, b| {
13012            a.depth.cmp(&b.depth).then(
13013                a.start_row
13014                    .cmp(&b.start_row)
13015                    .then(a.end_row.cmp(&b.end_row)),
13016            )
13017        });
13018        indent_guides
13019    });
13020
13021    if let Some(expected) = active_indices {
13022        let active_indices = cx.update_editor(|editor, cx| {
13023            let snapshot = editor.snapshot(cx).display_snapshot;
13024            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13025        });
13026
13027        assert_eq!(
13028            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13029            expected,
13030            "Active indent guide indices do not match"
13031        );
13032    }
13033
13034    let expected: Vec<_> = expected
13035        .into_iter()
13036        .map(|guide| MultiBufferIndentGuide {
13037            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13038            buffer: guide,
13039        })
13040        .collect();
13041
13042    assert_eq!(indent_guides, expected, "Indent guides do not match");
13043}
13044
13045fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13046    IndentGuide {
13047        buffer_id,
13048        start_row,
13049        end_row,
13050        depth,
13051        tab_size: 4,
13052        settings: IndentGuideSettings {
13053            enabled: true,
13054            line_width: 1,
13055            active_line_width: 1,
13056            ..Default::default()
13057        },
13058    }
13059}
13060
13061#[gpui::test]
13062async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13063    let (buffer_id, mut cx) = setup_indent_guides_editor(
13064        &"
13065    fn main() {
13066        let a = 1;
13067    }"
13068        .unindent(),
13069        cx,
13070    )
13071    .await;
13072
13073    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13074}
13075
13076#[gpui::test]
13077async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13078    let (buffer_id, mut cx) = setup_indent_guides_editor(
13079        &"
13080    fn main() {
13081        let a = 1;
13082        let b = 2;
13083    }"
13084        .unindent(),
13085        cx,
13086    )
13087    .await;
13088
13089    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13090}
13091
13092#[gpui::test]
13093async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13094    let (buffer_id, mut cx) = setup_indent_guides_editor(
13095        &"
13096    fn main() {
13097        let a = 1;
13098        if a == 3 {
13099            let b = 2;
13100        } else {
13101            let c = 3;
13102        }
13103    }"
13104        .unindent(),
13105        cx,
13106    )
13107    .await;
13108
13109    assert_indent_guides(
13110        0..8,
13111        vec![
13112            indent_guide(buffer_id, 1, 6, 0),
13113            indent_guide(buffer_id, 3, 3, 1),
13114            indent_guide(buffer_id, 5, 5, 1),
13115        ],
13116        None,
13117        &mut cx,
13118    );
13119}
13120
13121#[gpui::test]
13122async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13123    let (buffer_id, mut cx) = setup_indent_guides_editor(
13124        &"
13125    fn main() {
13126        let a = 1;
13127            let b = 2;
13128        let c = 3;
13129    }"
13130        .unindent(),
13131        cx,
13132    )
13133    .await;
13134
13135    assert_indent_guides(
13136        0..5,
13137        vec![
13138            indent_guide(buffer_id, 1, 3, 0),
13139            indent_guide(buffer_id, 2, 2, 1),
13140        ],
13141        None,
13142        &mut cx,
13143    );
13144}
13145
13146#[gpui::test]
13147async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13148    let (buffer_id, mut cx) = setup_indent_guides_editor(
13149        &"
13150        fn main() {
13151            let a = 1;
13152
13153            let c = 3;
13154        }"
13155        .unindent(),
13156        cx,
13157    )
13158    .await;
13159
13160    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13161}
13162
13163#[gpui::test]
13164async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13165    let (buffer_id, mut cx) = setup_indent_guides_editor(
13166        &"
13167        fn main() {
13168            let a = 1;
13169
13170            let c = 3;
13171
13172            if a == 3 {
13173                let b = 2;
13174            } else {
13175                let c = 3;
13176            }
13177        }"
13178        .unindent(),
13179        cx,
13180    )
13181    .await;
13182
13183    assert_indent_guides(
13184        0..11,
13185        vec![
13186            indent_guide(buffer_id, 1, 9, 0),
13187            indent_guide(buffer_id, 6, 6, 1),
13188            indent_guide(buffer_id, 8, 8, 1),
13189        ],
13190        None,
13191        &mut cx,
13192    );
13193}
13194
13195#[gpui::test]
13196async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13197    let (buffer_id, mut cx) = setup_indent_guides_editor(
13198        &"
13199        fn main() {
13200            let a = 1;
13201
13202            let c = 3;
13203
13204            if a == 3 {
13205                let b = 2;
13206            } else {
13207                let c = 3;
13208            }
13209        }"
13210        .unindent(),
13211        cx,
13212    )
13213    .await;
13214
13215    assert_indent_guides(
13216        1..11,
13217        vec![
13218            indent_guide(buffer_id, 1, 9, 0),
13219            indent_guide(buffer_id, 6, 6, 1),
13220            indent_guide(buffer_id, 8, 8, 1),
13221        ],
13222        None,
13223        &mut cx,
13224    );
13225}
13226
13227#[gpui::test]
13228async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13229    let (buffer_id, mut cx) = setup_indent_guides_editor(
13230        &"
13231        fn main() {
13232            let a = 1;
13233
13234            let c = 3;
13235
13236            if a == 3 {
13237                let b = 2;
13238            } else {
13239                let c = 3;
13240            }
13241        }"
13242        .unindent(),
13243        cx,
13244    )
13245    .await;
13246
13247    assert_indent_guides(
13248        1..10,
13249        vec![
13250            indent_guide(buffer_id, 1, 9, 0),
13251            indent_guide(buffer_id, 6, 6, 1),
13252            indent_guide(buffer_id, 8, 8, 1),
13253        ],
13254        None,
13255        &mut cx,
13256    );
13257}
13258
13259#[gpui::test]
13260async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13261    let (buffer_id, mut cx) = setup_indent_guides_editor(
13262        &"
13263        block1
13264            block2
13265                block3
13266                    block4
13267            block2
13268        block1
13269        block1"
13270            .unindent(),
13271        cx,
13272    )
13273    .await;
13274
13275    assert_indent_guides(
13276        1..10,
13277        vec![
13278            indent_guide(buffer_id, 1, 4, 0),
13279            indent_guide(buffer_id, 2, 3, 1),
13280            indent_guide(buffer_id, 3, 3, 2),
13281        ],
13282        None,
13283        &mut cx,
13284    );
13285}
13286
13287#[gpui::test]
13288async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13289    let (buffer_id, mut cx) = setup_indent_guides_editor(
13290        &"
13291        block1
13292            block2
13293                block3
13294
13295        block1
13296        block1"
13297            .unindent(),
13298        cx,
13299    )
13300    .await;
13301
13302    assert_indent_guides(
13303        0..6,
13304        vec![
13305            indent_guide(buffer_id, 1, 2, 0),
13306            indent_guide(buffer_id, 2, 2, 1),
13307        ],
13308        None,
13309        &mut cx,
13310    );
13311}
13312
13313#[gpui::test]
13314async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13315    let (buffer_id, mut cx) = setup_indent_guides_editor(
13316        &"
13317        block1
13318
13319
13320
13321            block2
13322        "
13323        .unindent(),
13324        cx,
13325    )
13326    .await;
13327
13328    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13329}
13330
13331#[gpui::test]
13332async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13333    let (buffer_id, mut cx) = setup_indent_guides_editor(
13334        &"
13335        def a:
13336        \tb = 3
13337        \tif True:
13338        \t\tc = 4
13339        \t\td = 5
13340        \tprint(b)
13341        "
13342        .unindent(),
13343        cx,
13344    )
13345    .await;
13346
13347    assert_indent_guides(
13348        0..6,
13349        vec![
13350            indent_guide(buffer_id, 1, 6, 0),
13351            indent_guide(buffer_id, 3, 4, 1),
13352        ],
13353        None,
13354        &mut cx,
13355    );
13356}
13357
13358#[gpui::test]
13359async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13360    let (buffer_id, mut cx) = setup_indent_guides_editor(
13361        &"
13362    fn main() {
13363        let a = 1;
13364    }"
13365        .unindent(),
13366        cx,
13367    )
13368    .await;
13369
13370    cx.update_editor(|editor, cx| {
13371        editor.change_selections(None, cx, |s| {
13372            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13373        });
13374    });
13375
13376    assert_indent_guides(
13377        0..3,
13378        vec![indent_guide(buffer_id, 1, 1, 0)],
13379        Some(vec![0]),
13380        &mut cx,
13381    );
13382}
13383
13384#[gpui::test]
13385async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13386    let (buffer_id, mut cx) = setup_indent_guides_editor(
13387        &"
13388    fn main() {
13389        if 1 == 2 {
13390            let a = 1;
13391        }
13392    }"
13393        .unindent(),
13394        cx,
13395    )
13396    .await;
13397
13398    cx.update_editor(|editor, cx| {
13399        editor.change_selections(None, cx, |s| {
13400            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13401        });
13402    });
13403
13404    assert_indent_guides(
13405        0..4,
13406        vec![
13407            indent_guide(buffer_id, 1, 3, 0),
13408            indent_guide(buffer_id, 2, 2, 1),
13409        ],
13410        Some(vec![1]),
13411        &mut cx,
13412    );
13413
13414    cx.update_editor(|editor, cx| {
13415        editor.change_selections(None, cx, |s| {
13416            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13417        });
13418    });
13419
13420    assert_indent_guides(
13421        0..4,
13422        vec![
13423            indent_guide(buffer_id, 1, 3, 0),
13424            indent_guide(buffer_id, 2, 2, 1),
13425        ],
13426        Some(vec![1]),
13427        &mut cx,
13428    );
13429
13430    cx.update_editor(|editor, cx| {
13431        editor.change_selections(None, cx, |s| {
13432            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13433        });
13434    });
13435
13436    assert_indent_guides(
13437        0..4,
13438        vec![
13439            indent_guide(buffer_id, 1, 3, 0),
13440            indent_guide(buffer_id, 2, 2, 1),
13441        ],
13442        Some(vec![0]),
13443        &mut cx,
13444    );
13445}
13446
13447#[gpui::test]
13448async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13449    let (buffer_id, mut cx) = setup_indent_guides_editor(
13450        &"
13451    fn main() {
13452        let a = 1;
13453
13454        let b = 2;
13455    }"
13456        .unindent(),
13457        cx,
13458    )
13459    .await;
13460
13461    cx.update_editor(|editor, cx| {
13462        editor.change_selections(None, cx, |s| {
13463            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13464        });
13465    });
13466
13467    assert_indent_guides(
13468        0..5,
13469        vec![indent_guide(buffer_id, 1, 3, 0)],
13470        Some(vec![0]),
13471        &mut cx,
13472    );
13473}
13474
13475#[gpui::test]
13476async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13477    let (buffer_id, mut cx) = setup_indent_guides_editor(
13478        &"
13479    def m:
13480        a = 1
13481        pass"
13482            .unindent(),
13483        cx,
13484    )
13485    .await;
13486
13487    cx.update_editor(|editor, cx| {
13488        editor.change_selections(None, cx, |s| {
13489            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13490        });
13491    });
13492
13493    assert_indent_guides(
13494        0..3,
13495        vec![indent_guide(buffer_id, 1, 2, 0)],
13496        Some(vec![0]),
13497        &mut cx,
13498    );
13499}
13500
13501#[gpui::test]
13502fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13503    init_test(cx, |_| {});
13504
13505    let editor = cx.add_window(|cx| {
13506        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13507        build_editor(buffer, cx)
13508    });
13509
13510    let render_args = Arc::new(Mutex::new(None));
13511    let snapshot = editor
13512        .update(cx, |editor, cx| {
13513            let snapshot = editor.buffer().read(cx).snapshot(cx);
13514            let range =
13515                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13516
13517            struct RenderArgs {
13518                row: MultiBufferRow,
13519                folded: bool,
13520                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13521            }
13522
13523            let crease = Crease::inline(
13524                range,
13525                FoldPlaceholder::test(),
13526                {
13527                    let toggle_callback = render_args.clone();
13528                    move |row, folded, callback, _cx| {
13529                        *toggle_callback.lock() = Some(RenderArgs {
13530                            row,
13531                            folded,
13532                            callback,
13533                        });
13534                        div()
13535                    }
13536                },
13537                |_row, _folded, _cx| div(),
13538            );
13539
13540            editor.insert_creases(Some(crease), cx);
13541            let snapshot = editor.snapshot(cx);
13542            let _div =
13543                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13544            snapshot
13545        })
13546        .unwrap();
13547
13548    let render_args = render_args.lock().take().unwrap();
13549    assert_eq!(render_args.row, MultiBufferRow(1));
13550    assert!(!render_args.folded);
13551    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13552
13553    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13554        .unwrap();
13555    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13556    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13557
13558    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13559        .unwrap();
13560    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13561    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13562}
13563
13564#[gpui::test]
13565async fn test_input_text(cx: &mut gpui::TestAppContext) {
13566    init_test(cx, |_| {});
13567    let mut cx = EditorTestContext::new(cx).await;
13568
13569    cx.set_state(
13570        &r#"ˇone
13571        two
13572
13573        three
13574        fourˇ
13575        five
13576
13577        siˇx"#
13578            .unindent(),
13579    );
13580
13581    cx.dispatch_action(HandleInput(String::new()));
13582    cx.assert_editor_state(
13583        &r#"ˇone
13584        two
13585
13586        three
13587        fourˇ
13588        five
13589
13590        siˇx"#
13591            .unindent(),
13592    );
13593
13594    cx.dispatch_action(HandleInput("AAAA".to_string()));
13595    cx.assert_editor_state(
13596        &r#"AAAAˇone
13597        two
13598
13599        three
13600        fourAAAAˇ
13601        five
13602
13603        siAAAAˇx"#
13604            .unindent(),
13605    );
13606}
13607
13608#[gpui::test]
13609async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13610    init_test(cx, |_| {});
13611
13612    let mut cx = EditorTestContext::new(cx).await;
13613    cx.set_state(
13614        r#"let foo = 1;
13615let foo = 2;
13616let foo = 3;
13617let fooˇ = 4;
13618let foo = 5;
13619let foo = 6;
13620let foo = 7;
13621let foo = 8;
13622let foo = 9;
13623let foo = 10;
13624let foo = 11;
13625let foo = 12;
13626let foo = 13;
13627let foo = 14;
13628let foo = 15;"#,
13629    );
13630
13631    cx.update_editor(|e, cx| {
13632        assert_eq!(
13633            e.next_scroll_position,
13634            NextScrollCursorCenterTopBottom::Center,
13635            "Default next scroll direction is center",
13636        );
13637
13638        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13639        assert_eq!(
13640            e.next_scroll_position,
13641            NextScrollCursorCenterTopBottom::Top,
13642            "After center, next scroll direction should be top",
13643        );
13644
13645        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13646        assert_eq!(
13647            e.next_scroll_position,
13648            NextScrollCursorCenterTopBottom::Bottom,
13649            "After top, next scroll direction should be bottom",
13650        );
13651
13652        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13653        assert_eq!(
13654            e.next_scroll_position,
13655            NextScrollCursorCenterTopBottom::Center,
13656            "After bottom, scrolling should start over",
13657        );
13658
13659        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13660        assert_eq!(
13661            e.next_scroll_position,
13662            NextScrollCursorCenterTopBottom::Top,
13663            "Scrolling continues if retriggered fast enough"
13664        );
13665    });
13666
13667    cx.executor()
13668        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13669    cx.executor().run_until_parked();
13670    cx.update_editor(|e, _| {
13671        assert_eq!(
13672            e.next_scroll_position,
13673            NextScrollCursorCenterTopBottom::Center,
13674            "If scrolling is not triggered fast enough, it should reset"
13675        );
13676    });
13677}
13678
13679#[gpui::test]
13680async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13681    init_test(cx, |_| {});
13682    let mut cx = EditorLspTestContext::new_rust(
13683        lsp::ServerCapabilities {
13684            definition_provider: Some(lsp::OneOf::Left(true)),
13685            references_provider: Some(lsp::OneOf::Left(true)),
13686            ..lsp::ServerCapabilities::default()
13687        },
13688        cx,
13689    )
13690    .await;
13691
13692    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13693        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13694            move |params, _| async move {
13695                if empty_go_to_definition {
13696                    Ok(None)
13697                } else {
13698                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13699                        uri: params.text_document_position_params.text_document.uri,
13700                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13701                    })))
13702                }
13703            },
13704        );
13705        let references =
13706            cx.lsp
13707                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13708                    Ok(Some(vec![lsp::Location {
13709                        uri: params.text_document_position.text_document.uri,
13710                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13711                    }]))
13712                });
13713        (go_to_definition, references)
13714    };
13715
13716    cx.set_state(
13717        &r#"fn one() {
13718            let mut a = ˇtwo();
13719        }
13720
13721        fn two() {}"#
13722            .unindent(),
13723    );
13724    set_up_lsp_handlers(false, &mut cx);
13725    let navigated = cx
13726        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13727        .await
13728        .expect("Failed to navigate to definition");
13729    assert_eq!(
13730        navigated,
13731        Navigated::Yes,
13732        "Should have navigated to definition from the GetDefinition response"
13733    );
13734    cx.assert_editor_state(
13735        &r#"fn one() {
13736            let mut a = two();
13737        }
13738
13739        fn «twoˇ»() {}"#
13740            .unindent(),
13741    );
13742
13743    let editors = cx.update_workspace(|workspace, cx| {
13744        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13745    });
13746    cx.update_editor(|_, test_editor_cx| {
13747        assert_eq!(
13748            editors.len(),
13749            1,
13750            "Initially, only one, test, editor should be open in the workspace"
13751        );
13752        assert_eq!(
13753            test_editor_cx.view(),
13754            editors.last().expect("Asserted len is 1")
13755        );
13756    });
13757
13758    set_up_lsp_handlers(true, &mut cx);
13759    let navigated = cx
13760        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13761        .await
13762        .expect("Failed to navigate to lookup references");
13763    assert_eq!(
13764        navigated,
13765        Navigated::Yes,
13766        "Should have navigated to references as a fallback after empty GoToDefinition response"
13767    );
13768    // We should not change the selections in the existing file,
13769    // if opening another milti buffer with the references
13770    cx.assert_editor_state(
13771        &r#"fn one() {
13772            let mut a = two();
13773        }
13774
13775        fn «twoˇ»() {}"#
13776            .unindent(),
13777    );
13778    let editors = cx.update_workspace(|workspace, cx| {
13779        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13780    });
13781    cx.update_editor(|_, test_editor_cx| {
13782        assert_eq!(
13783            editors.len(),
13784            2,
13785            "After falling back to references search, we open a new editor with the results"
13786        );
13787        let references_fallback_text = editors
13788            .into_iter()
13789            .find(|new_editor| new_editor != test_editor_cx.view())
13790            .expect("Should have one non-test editor now")
13791            .read(test_editor_cx)
13792            .text(test_editor_cx);
13793        assert_eq!(
13794            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13795            "Should use the range from the references response and not the GoToDefinition one"
13796        );
13797    });
13798}
13799
13800#[gpui::test]
13801async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13802    init_test(cx, |_| {});
13803
13804    let language = Arc::new(Language::new(
13805        LanguageConfig::default(),
13806        Some(tree_sitter_rust::LANGUAGE.into()),
13807    ));
13808
13809    let text = r#"
13810        #[cfg(test)]
13811        mod tests() {
13812            #[test]
13813            fn runnable_1() {
13814                let a = 1;
13815            }
13816
13817            #[test]
13818            fn runnable_2() {
13819                let a = 1;
13820                let b = 2;
13821            }
13822        }
13823    "#
13824    .unindent();
13825
13826    let fs = FakeFs::new(cx.executor());
13827    fs.insert_file("/file.rs", Default::default()).await;
13828
13829    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13830    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13831    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13832    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13833    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13834
13835    let editor = cx.new_view(|cx| {
13836        Editor::new(
13837            EditorMode::Full,
13838            multi_buffer,
13839            Some(project.clone()),
13840            true,
13841            cx,
13842        )
13843    });
13844
13845    editor.update(cx, |editor, cx| {
13846        editor.tasks.insert(
13847            (buffer.read(cx).remote_id(), 3),
13848            RunnableTasks {
13849                templates: vec![],
13850                offset: MultiBufferOffset(43),
13851                column: 0,
13852                extra_variables: HashMap::default(),
13853                context_range: BufferOffset(43)..BufferOffset(85),
13854            },
13855        );
13856        editor.tasks.insert(
13857            (buffer.read(cx).remote_id(), 8),
13858            RunnableTasks {
13859                templates: vec![],
13860                offset: MultiBufferOffset(86),
13861                column: 0,
13862                extra_variables: HashMap::default(),
13863                context_range: BufferOffset(86)..BufferOffset(191),
13864            },
13865        );
13866
13867        // Test finding task when cursor is inside function body
13868        editor.change_selections(None, cx, |s| {
13869            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13870        });
13871        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13872        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13873
13874        // Test finding task when cursor is on function name
13875        editor.change_selections(None, cx, |s| {
13876            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13877        });
13878        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13879        assert_eq!(row, 8, "Should find task when cursor is on function name");
13880    });
13881}
13882
13883fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13884    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13885    point..point
13886}
13887
13888fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13889    let (text, ranges) = marked_text_ranges(marked_text, true);
13890    assert_eq!(view.text(cx), text);
13891    assert_eq!(
13892        view.selections.ranges(cx),
13893        ranges,
13894        "Assert selections are {}",
13895        marked_text
13896    );
13897}
13898
13899pub fn handle_signature_help_request(
13900    cx: &mut EditorLspTestContext,
13901    mocked_response: lsp::SignatureHelp,
13902) -> impl Future<Output = ()> {
13903    let mut request =
13904        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13905            let mocked_response = mocked_response.clone();
13906            async move { Ok(Some(mocked_response)) }
13907        });
13908
13909    async move {
13910        request.next().await;
13911    }
13912}
13913
13914/// Handle completion request passing a marked string specifying where the completion
13915/// should be triggered from using '|' character, what range should be replaced, and what completions
13916/// should be returned using '<' and '>' to delimit the range
13917pub fn handle_completion_request(
13918    cx: &mut EditorLspTestContext,
13919    marked_string: &str,
13920    completions: Vec<&'static str>,
13921    counter: Arc<AtomicUsize>,
13922) -> impl Future<Output = ()> {
13923    let complete_from_marker: TextRangeMarker = '|'.into();
13924    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13925    let (_, mut marked_ranges) = marked_text_ranges_by(
13926        marked_string,
13927        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13928    );
13929
13930    let complete_from_position =
13931        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13932    let replace_range =
13933        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13934
13935    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13936        let completions = completions.clone();
13937        counter.fetch_add(1, atomic::Ordering::Release);
13938        async move {
13939            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13940            assert_eq!(
13941                params.text_document_position.position,
13942                complete_from_position
13943            );
13944            Ok(Some(lsp::CompletionResponse::Array(
13945                completions
13946                    .iter()
13947                    .map(|completion_text| lsp::CompletionItem {
13948                        label: completion_text.to_string(),
13949                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13950                            range: replace_range,
13951                            new_text: completion_text.to_string(),
13952                        })),
13953                        ..Default::default()
13954                    })
13955                    .collect(),
13956            )))
13957        }
13958    });
13959
13960    async move {
13961        request.next().await;
13962    }
13963}
13964
13965fn handle_resolve_completion_request(
13966    cx: &mut EditorLspTestContext,
13967    edits: Option<Vec<(&'static str, &'static str)>>,
13968) -> impl Future<Output = ()> {
13969    let edits = edits.map(|edits| {
13970        edits
13971            .iter()
13972            .map(|(marked_string, new_text)| {
13973                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13974                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13975                lsp::TextEdit::new(replace_range, new_text.to_string())
13976            })
13977            .collect::<Vec<_>>()
13978    });
13979
13980    let mut request =
13981        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13982            let edits = edits.clone();
13983            async move {
13984                Ok(lsp::CompletionItem {
13985                    additional_text_edits: edits,
13986                    ..Default::default()
13987                })
13988            }
13989        });
13990
13991    async move {
13992        request.next().await;
13993    }
13994}
13995
13996pub(crate) fn update_test_language_settings(
13997    cx: &mut TestAppContext,
13998    f: impl Fn(&mut AllLanguageSettingsContent),
13999) {
14000    cx.update(|cx| {
14001        SettingsStore::update_global(cx, |store, cx| {
14002            store.update_user_settings::<AllLanguageSettings>(cx, f);
14003        });
14004    });
14005}
14006
14007pub(crate) fn update_test_project_settings(
14008    cx: &mut TestAppContext,
14009    f: impl Fn(&mut ProjectSettings),
14010) {
14011    cx.update(|cx| {
14012        SettingsStore::update_global(cx, |store, cx| {
14013            store.update_user_settings::<ProjectSettings>(cx, f);
14014        });
14015    });
14016}
14017
14018pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14019    cx.update(|cx| {
14020        assets::Assets.load_test_fonts(cx);
14021        let store = SettingsStore::test(cx);
14022        cx.set_global(store);
14023        theme::init(theme::LoadThemes::JustBase, cx);
14024        release_channel::init(SemanticVersion::default(), cx);
14025        client::init_settings(cx);
14026        language::init(cx);
14027        Project::init_settings(cx);
14028        workspace::init_settings(cx);
14029        crate::init(cx);
14030    });
14031
14032    update_test_language_settings(cx, f);
14033}
14034
14035#[track_caller]
14036fn assert_hunk_revert(
14037    not_reverted_text_with_selections: &str,
14038    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14039    expected_reverted_text_with_selections: &str,
14040    base_text: &str,
14041    cx: &mut EditorLspTestContext,
14042) {
14043    cx.set_state(not_reverted_text_with_selections);
14044    cx.set_diff_base(base_text);
14045    cx.executor().run_until_parked();
14046
14047    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14048        let snapshot = editor.snapshot(cx);
14049        let reverted_hunk_statuses = snapshot
14050            .diff_map
14051            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14052            .map(|hunk| hunk_status(&hunk))
14053            .collect::<Vec<_>>();
14054
14055        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14056        reverted_hunk_statuses
14057    });
14058    cx.executor().run_until_parked();
14059    cx.assert_editor_state(expected_reverted_text_with_selections);
14060    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14061}