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, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    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::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;
   35use std::sync::atomic::AtomicUsize;
   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(Some(&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(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3357    executor.run_until_parked();
 3358
 3359    cx.update_editor(|editor, cx| {
 3360        assert_eq!(
 3361            editor
 3362                .buffer()
 3363                .read(cx)
 3364                .snapshot(cx)
 3365                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3366                .collect::<Vec<_>>(),
 3367            Vec::new(),
 3368            "Should not have any diffs for files with custom newlines"
 3369        );
 3370    });
 3371}
 3372
 3373#[gpui::test]
 3374async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3375    init_test(cx, |_| {});
 3376
 3377    let mut cx = EditorTestContext::new(cx).await;
 3378
 3379    // Test sort_lines_case_insensitive()
 3380    cx.set_state(indoc! {"
 3381        «z
 3382        y
 3383        x
 3384        Z
 3385        Y
 3386        Xˇ»
 3387    "});
 3388    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3389    cx.assert_editor_state(indoc! {"
 3390        «x
 3391        X
 3392        y
 3393        Y
 3394        z
 3395        Zˇ»
 3396    "});
 3397
 3398    // Test reverse_lines()
 3399    cx.set_state(indoc! {"
 3400        «5
 3401        4
 3402        3
 3403        2
 3404        1ˇ»
 3405    "});
 3406    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3407    cx.assert_editor_state(indoc! {"
 3408        «1
 3409        2
 3410        3
 3411        4
 3412        5ˇ»
 3413    "});
 3414
 3415    // Skip testing shuffle_line()
 3416
 3417    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3418    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3419
 3420    // Don't manipulate when cursor is on single line, but expand the selection
 3421    cx.set_state(indoc! {"
 3422        ddˇdd
 3423        ccc
 3424        bb
 3425        a
 3426    "});
 3427    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3428    cx.assert_editor_state(indoc! {"
 3429        «ddddˇ»
 3430        ccc
 3431        bb
 3432        a
 3433    "});
 3434
 3435    // Basic manipulate case
 3436    // Start selection moves to column 0
 3437    // End of selection shrinks to fit shorter line
 3438    cx.set_state(indoc! {"
 3439        dd«d
 3440        ccc
 3441        bb
 3442        aaaaaˇ»
 3443    "});
 3444    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3445    cx.assert_editor_state(indoc! {"
 3446        «aaaaa
 3447        bb
 3448        ccc
 3449        dddˇ»
 3450    "});
 3451
 3452    // Manipulate case with newlines
 3453    cx.set_state(indoc! {"
 3454        dd«d
 3455        ccc
 3456
 3457        bb
 3458        aaaaa
 3459
 3460        ˇ»
 3461    "});
 3462    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3463    cx.assert_editor_state(indoc! {"
 3464        «
 3465
 3466        aaaaa
 3467        bb
 3468        ccc
 3469        dddˇ»
 3470
 3471    "});
 3472
 3473    // Adding new line
 3474    cx.set_state(indoc! {"
 3475        aa«a
 3476        bbˇ»b
 3477    "});
 3478    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3479    cx.assert_editor_state(indoc! {"
 3480        «aaa
 3481        bbb
 3482        added_lineˇ»
 3483    "});
 3484
 3485    // Removing line
 3486    cx.set_state(indoc! {"
 3487        aa«a
 3488        bbbˇ»
 3489    "});
 3490    cx.update_editor(|e, cx| {
 3491        e.manipulate_lines(cx, |lines| {
 3492            lines.pop();
 3493        })
 3494    });
 3495    cx.assert_editor_state(indoc! {"
 3496        «aaaˇ»
 3497    "});
 3498
 3499    // Removing all lines
 3500    cx.set_state(indoc! {"
 3501        aa«a
 3502        bbbˇ»
 3503    "});
 3504    cx.update_editor(|e, cx| {
 3505        e.manipulate_lines(cx, |lines| {
 3506            lines.drain(..);
 3507        })
 3508    });
 3509    cx.assert_editor_state(indoc! {"
 3510        ˇ
 3511    "});
 3512}
 3513
 3514#[gpui::test]
 3515async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3516    init_test(cx, |_| {});
 3517
 3518    let mut cx = EditorTestContext::new(cx).await;
 3519
 3520    // Consider continuous selection as single selection
 3521    cx.set_state(indoc! {"
 3522        Aaa«aa
 3523        cˇ»c«c
 3524        bb
 3525        aaaˇ»aa
 3526    "});
 3527    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3528    cx.assert_editor_state(indoc! {"
 3529        «Aaaaa
 3530        ccc
 3531        bb
 3532        aaaaaˇ»
 3533    "});
 3534
 3535    cx.set_state(indoc! {"
 3536        Aaa«aa
 3537        cˇ»c«c
 3538        bb
 3539        aaaˇ»aa
 3540    "});
 3541    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3542    cx.assert_editor_state(indoc! {"
 3543        «Aaaaa
 3544        ccc
 3545        bbˇ»
 3546    "});
 3547
 3548    // Consider non continuous selection as distinct dedup operations
 3549    cx.set_state(indoc! {"
 3550        «aaaaa
 3551        bb
 3552        aaaaa
 3553        aaaaaˇ»
 3554
 3555        aaa«aaˇ»
 3556    "});
 3557    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3558    cx.assert_editor_state(indoc! {"
 3559        «aaaaa
 3560        bbˇ»
 3561
 3562        «aaaaaˇ»
 3563    "});
 3564}
 3565
 3566#[gpui::test]
 3567async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3568    init_test(cx, |_| {});
 3569
 3570    let mut cx = EditorTestContext::new(cx).await;
 3571
 3572    cx.set_state(indoc! {"
 3573        «Aaa
 3574        aAa
 3575        Aaaˇ»
 3576    "});
 3577    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3578    cx.assert_editor_state(indoc! {"
 3579        «Aaa
 3580        aAaˇ»
 3581    "});
 3582
 3583    cx.set_state(indoc! {"
 3584        «Aaa
 3585        aAa
 3586        aaAˇ»
 3587    "});
 3588    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3589    cx.assert_editor_state(indoc! {"
 3590        «Aaaˇ»
 3591    "});
 3592}
 3593
 3594#[gpui::test]
 3595async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3596    init_test(cx, |_| {});
 3597
 3598    let mut cx = EditorTestContext::new(cx).await;
 3599
 3600    // Manipulate with multiple selections on a single line
 3601    cx.set_state(indoc! {"
 3602        dd«dd
 3603        cˇ»c«c
 3604        bb
 3605        aaaˇ»aa
 3606    "});
 3607    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3608    cx.assert_editor_state(indoc! {"
 3609        «aaaaa
 3610        bb
 3611        ccc
 3612        ddddˇ»
 3613    "});
 3614
 3615    // Manipulate with multiple disjoin selections
 3616    cx.set_state(indoc! {"
 3617 3618        4
 3619        3
 3620        2
 3621        1ˇ»
 3622
 3623        dd«dd
 3624        ccc
 3625        bb
 3626        aaaˇ»aa
 3627    "});
 3628    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3629    cx.assert_editor_state(indoc! {"
 3630        «1
 3631        2
 3632        3
 3633        4
 3634        5ˇ»
 3635
 3636        «aaaaa
 3637        bb
 3638        ccc
 3639        ddddˇ»
 3640    "});
 3641
 3642    // Adding lines on each selection
 3643    cx.set_state(indoc! {"
 3644 3645        1ˇ»
 3646
 3647        bb«bb
 3648        aaaˇ»aa
 3649    "});
 3650    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3651    cx.assert_editor_state(indoc! {"
 3652        «2
 3653        1
 3654        added lineˇ»
 3655
 3656        «bbbb
 3657        aaaaa
 3658        added lineˇ»
 3659    "});
 3660
 3661    // Removing lines on each selection
 3662    cx.set_state(indoc! {"
 3663 3664        1ˇ»
 3665
 3666        bb«bb
 3667        aaaˇ»aa
 3668    "});
 3669    cx.update_editor(|e, cx| {
 3670        e.manipulate_lines(cx, |lines| {
 3671            lines.pop();
 3672        })
 3673    });
 3674    cx.assert_editor_state(indoc! {"
 3675        «2ˇ»
 3676
 3677        «bbbbˇ»
 3678    "});
 3679}
 3680
 3681#[gpui::test]
 3682async fn test_manipulate_text(cx: &mut TestAppContext) {
 3683    init_test(cx, |_| {});
 3684
 3685    let mut cx = EditorTestContext::new(cx).await;
 3686
 3687    // Test convert_to_upper_case()
 3688    cx.set_state(indoc! {"
 3689        «hello worldˇ»
 3690    "});
 3691    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3692    cx.assert_editor_state(indoc! {"
 3693        «HELLO WORLDˇ»
 3694    "});
 3695
 3696    // Test convert_to_lower_case()
 3697    cx.set_state(indoc! {"
 3698        «HELLO WORLDˇ»
 3699    "});
 3700    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3701    cx.assert_editor_state(indoc! {"
 3702        «hello worldˇ»
 3703    "});
 3704
 3705    // Test multiple line, single selection case
 3706    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3707    cx.set_state(indoc! {"
 3708        «The quick brown
 3709        fox jumps over
 3710        the lazy dogˇ»
 3711    "});
 3712    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3713    cx.assert_editor_state(indoc! {"
 3714        «The Quick Brown
 3715        Fox Jumps Over
 3716        The Lazy Dogˇ»
 3717    "});
 3718
 3719    // Test multiple line, single selection case
 3720    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3721    cx.set_state(indoc! {"
 3722        «The quick brown
 3723        fox jumps over
 3724        the lazy dogˇ»
 3725    "});
 3726    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3727    cx.assert_editor_state(indoc! {"
 3728        «TheQuickBrown
 3729        FoxJumpsOver
 3730        TheLazyDogˇ»
 3731    "});
 3732
 3733    // From here on out, test more complex cases of manipulate_text()
 3734
 3735    // Test no selection case - should affect words cursors are in
 3736    // Cursor at beginning, middle, and end of word
 3737    cx.set_state(indoc! {"
 3738        ˇhello big beauˇtiful worldˇ
 3739    "});
 3740    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3741    cx.assert_editor_state(indoc! {"
 3742        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3743    "});
 3744
 3745    // Test multiple selections on a single line and across multiple lines
 3746    cx.set_state(indoc! {"
 3747        «Theˇ» quick «brown
 3748        foxˇ» jumps «overˇ»
 3749        the «lazyˇ» dog
 3750    "});
 3751    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3752    cx.assert_editor_state(indoc! {"
 3753        «THEˇ» quick «BROWN
 3754        FOXˇ» jumps «OVERˇ»
 3755        the «LAZYˇ» dog
 3756    "});
 3757
 3758    // Test case where text length grows
 3759    cx.set_state(indoc! {"
 3760        «tschüߡ»
 3761    "});
 3762    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3763    cx.assert_editor_state(indoc! {"
 3764        «TSCHÜSSˇ»
 3765    "});
 3766
 3767    // Test to make sure we don't crash when text shrinks
 3768    cx.set_state(indoc! {"
 3769        aaa_bbbˇ
 3770    "});
 3771    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3772    cx.assert_editor_state(indoc! {"
 3773        «aaaBbbˇ»
 3774    "});
 3775
 3776    // Test to make sure we all aware of the fact that each word can grow and shrink
 3777    // Final selections should be aware of this fact
 3778    cx.set_state(indoc! {"
 3779        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3780    "});
 3781    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3782    cx.assert_editor_state(indoc! {"
 3783        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3784    "});
 3785
 3786    cx.set_state(indoc! {"
 3787        «hElLo, WoRld!ˇ»
 3788    "});
 3789    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3790    cx.assert_editor_state(indoc! {"
 3791        «HeLlO, wOrLD!ˇ»
 3792    "});
 3793}
 3794
 3795#[gpui::test]
 3796fn test_duplicate_line(cx: &mut TestAppContext) {
 3797    init_test(cx, |_| {});
 3798
 3799    let view = cx.add_window(|cx| {
 3800        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3801        build_editor(buffer, cx)
 3802    });
 3803    _ = view.update(cx, |view, cx| {
 3804        view.change_selections(None, cx, |s| {
 3805            s.select_display_ranges([
 3806                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3807                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3808                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3809                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3810            ])
 3811        });
 3812        view.duplicate_line_down(&DuplicateLineDown, cx);
 3813        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3814        assert_eq!(
 3815            view.selections.display_ranges(cx),
 3816            vec![
 3817                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3818                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3819                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3820                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3821            ]
 3822        );
 3823    });
 3824
 3825    let view = cx.add_window(|cx| {
 3826        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3827        build_editor(buffer, cx)
 3828    });
 3829    _ = view.update(cx, |view, cx| {
 3830        view.change_selections(None, cx, |s| {
 3831            s.select_display_ranges([
 3832                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3834            ])
 3835        });
 3836        view.duplicate_line_down(&DuplicateLineDown, cx);
 3837        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3838        assert_eq!(
 3839            view.selections.display_ranges(cx),
 3840            vec![
 3841                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3842                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3843            ]
 3844        );
 3845    });
 3846
 3847    // With `move_upwards` the selections stay in place, except for
 3848    // the lines inserted above them
 3849    let view = cx.add_window(|cx| {
 3850        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3851        build_editor(buffer, cx)
 3852    });
 3853    _ = view.update(cx, |view, cx| {
 3854        view.change_selections(None, cx, |s| {
 3855            s.select_display_ranges([
 3856                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3857                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3858                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3859                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3860            ])
 3861        });
 3862        view.duplicate_line_up(&DuplicateLineUp, cx);
 3863        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3864        assert_eq!(
 3865            view.selections.display_ranges(cx),
 3866            vec![
 3867                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3869                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3870                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3871            ]
 3872        );
 3873    });
 3874
 3875    let view = cx.add_window(|cx| {
 3876        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3877        build_editor(buffer, cx)
 3878    });
 3879    _ = view.update(cx, |view, cx| {
 3880        view.change_selections(None, cx, |s| {
 3881            s.select_display_ranges([
 3882                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3883                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3884            ])
 3885        });
 3886        view.duplicate_line_up(&DuplicateLineUp, cx);
 3887        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3888        assert_eq!(
 3889            view.selections.display_ranges(cx),
 3890            vec![
 3891                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3892                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3893            ]
 3894        );
 3895    });
 3896}
 3897
 3898#[gpui::test]
 3899fn test_move_line_up_down(cx: &mut TestAppContext) {
 3900    init_test(cx, |_| {});
 3901
 3902    let view = cx.add_window(|cx| {
 3903        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3904        build_editor(buffer, cx)
 3905    });
 3906    _ = view.update(cx, |view, cx| {
 3907        view.fold_creases(
 3908            vec![
 3909                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3910                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3911                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3912            ],
 3913            true,
 3914            cx,
 3915        );
 3916        view.change_selections(None, cx, |s| {
 3917            s.select_display_ranges([
 3918                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3919                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3920                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3921                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3922            ])
 3923        });
 3924        assert_eq!(
 3925            view.display_text(cx),
 3926            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3927        );
 3928
 3929        view.move_line_up(&MoveLineUp, cx);
 3930        assert_eq!(
 3931            view.display_text(cx),
 3932            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3933        );
 3934        assert_eq!(
 3935            view.selections.display_ranges(cx),
 3936            vec![
 3937                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3938                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3939                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3940                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3941            ]
 3942        );
 3943    });
 3944
 3945    _ = view.update(cx, |view, cx| {
 3946        view.move_line_down(&MoveLineDown, cx);
 3947        assert_eq!(
 3948            view.display_text(cx),
 3949            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3950        );
 3951        assert_eq!(
 3952            view.selections.display_ranges(cx),
 3953            vec![
 3954                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3955                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3956                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3957                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3958            ]
 3959        );
 3960    });
 3961
 3962    _ = view.update(cx, |view, cx| {
 3963        view.move_line_down(&MoveLineDown, cx);
 3964        assert_eq!(
 3965            view.display_text(cx),
 3966            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3967        );
 3968        assert_eq!(
 3969            view.selections.display_ranges(cx),
 3970            vec![
 3971                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3972                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3973                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3974                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3975            ]
 3976        );
 3977    });
 3978
 3979    _ = view.update(cx, |view, cx| {
 3980        view.move_line_up(&MoveLineUp, cx);
 3981        assert_eq!(
 3982            view.display_text(cx),
 3983            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3984        );
 3985        assert_eq!(
 3986            view.selections.display_ranges(cx),
 3987            vec![
 3988                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3989                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3990                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3991                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3992            ]
 3993        );
 3994    });
 3995}
 3996
 3997#[gpui::test]
 3998fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3999    init_test(cx, |_| {});
 4000
 4001    let editor = cx.add_window(|cx| {
 4002        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4003        build_editor(buffer, cx)
 4004    });
 4005    _ = editor.update(cx, |editor, cx| {
 4006        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4007        editor.insert_blocks(
 4008            [BlockProperties {
 4009                style: BlockStyle::Fixed,
 4010                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4011                height: 1,
 4012                render: Arc::new(|_| div().into_any()),
 4013                priority: 0,
 4014            }],
 4015            Some(Autoscroll::fit()),
 4016            cx,
 4017        );
 4018        editor.change_selections(None, cx, |s| {
 4019            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4020        });
 4021        editor.move_line_down(&MoveLineDown, cx);
 4022    });
 4023}
 4024
 4025#[gpui::test]
 4026async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4027    init_test(cx, |_| {});
 4028
 4029    let mut cx = EditorTestContext::new(cx).await;
 4030    cx.set_state(
 4031        &"
 4032            ˇzero
 4033            one
 4034            two
 4035            three
 4036            four
 4037            five
 4038        "
 4039        .unindent(),
 4040    );
 4041
 4042    // Create a four-line block that replaces three lines of text.
 4043    cx.update_editor(|editor, cx| {
 4044        let snapshot = editor.snapshot(cx);
 4045        let snapshot = &snapshot.buffer_snapshot;
 4046        let placement = BlockPlacement::Replace(
 4047            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4048        );
 4049        editor.insert_blocks(
 4050            [BlockProperties {
 4051                placement,
 4052                height: 4,
 4053                style: BlockStyle::Sticky,
 4054                render: Arc::new(|_| gpui::div().into_any_element()),
 4055                priority: 0,
 4056            }],
 4057            None,
 4058            cx,
 4059        );
 4060    });
 4061
 4062    // Move down so that the cursor touches the block.
 4063    cx.update_editor(|editor, cx| {
 4064        editor.move_down(&Default::default(), cx);
 4065    });
 4066    cx.assert_editor_state(
 4067        &"
 4068            zero
 4069            «one
 4070            two
 4071            threeˇ»
 4072            four
 4073            five
 4074        "
 4075        .unindent(),
 4076    );
 4077
 4078    // Move down past the block.
 4079    cx.update_editor(|editor, cx| {
 4080        editor.move_down(&Default::default(), cx);
 4081    });
 4082    cx.assert_editor_state(
 4083        &"
 4084            zero
 4085            one
 4086            two
 4087            three
 4088            ˇfour
 4089            five
 4090        "
 4091        .unindent(),
 4092    );
 4093}
 4094
 4095#[gpui::test]
 4096fn test_transpose(cx: &mut TestAppContext) {
 4097    init_test(cx, |_| {});
 4098
 4099    _ = cx.add_window(|cx| {
 4100        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4101        editor.set_style(EditorStyle::default(), cx);
 4102        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4103        editor.transpose(&Default::default(), cx);
 4104        assert_eq!(editor.text(cx), "bac");
 4105        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4106
 4107        editor.transpose(&Default::default(), cx);
 4108        assert_eq!(editor.text(cx), "bca");
 4109        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4110
 4111        editor.transpose(&Default::default(), cx);
 4112        assert_eq!(editor.text(cx), "bac");
 4113        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4114
 4115        editor
 4116    });
 4117
 4118    _ = cx.add_window(|cx| {
 4119        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4120        editor.set_style(EditorStyle::default(), cx);
 4121        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4122        editor.transpose(&Default::default(), cx);
 4123        assert_eq!(editor.text(cx), "acb\nde");
 4124        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4125
 4126        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4127        editor.transpose(&Default::default(), cx);
 4128        assert_eq!(editor.text(cx), "acbd\ne");
 4129        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4130
 4131        editor.transpose(&Default::default(), cx);
 4132        assert_eq!(editor.text(cx), "acbde\n");
 4133        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4134
 4135        editor.transpose(&Default::default(), cx);
 4136        assert_eq!(editor.text(cx), "acbd\ne");
 4137        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4138
 4139        editor
 4140    });
 4141
 4142    _ = cx.add_window(|cx| {
 4143        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4144        editor.set_style(EditorStyle::default(), cx);
 4145        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4146        editor.transpose(&Default::default(), cx);
 4147        assert_eq!(editor.text(cx), "bacd\ne");
 4148        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4149
 4150        editor.transpose(&Default::default(), cx);
 4151        assert_eq!(editor.text(cx), "bcade\n");
 4152        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4153
 4154        editor.transpose(&Default::default(), cx);
 4155        assert_eq!(editor.text(cx), "bcda\ne");
 4156        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4157
 4158        editor.transpose(&Default::default(), cx);
 4159        assert_eq!(editor.text(cx), "bcade\n");
 4160        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4161
 4162        editor.transpose(&Default::default(), cx);
 4163        assert_eq!(editor.text(cx), "bcaed\n");
 4164        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4165
 4166        editor
 4167    });
 4168
 4169    _ = cx.add_window(|cx| {
 4170        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4171        editor.set_style(EditorStyle::default(), cx);
 4172        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4173        editor.transpose(&Default::default(), cx);
 4174        assert_eq!(editor.text(cx), "🏀🍐✋");
 4175        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4176
 4177        editor.transpose(&Default::default(), cx);
 4178        assert_eq!(editor.text(cx), "🏀✋🍐");
 4179        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4180
 4181        editor.transpose(&Default::default(), cx);
 4182        assert_eq!(editor.text(cx), "🏀🍐✋");
 4183        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4184
 4185        editor
 4186    });
 4187}
 4188
 4189#[gpui::test]
 4190async fn test_rewrap(cx: &mut TestAppContext) {
 4191    init_test(cx, |_| {});
 4192
 4193    let mut cx = EditorTestContext::new(cx).await;
 4194
 4195    let language_with_c_comments = Arc::new(Language::new(
 4196        LanguageConfig {
 4197            line_comments: vec!["// ".into()],
 4198            ..LanguageConfig::default()
 4199        },
 4200        None,
 4201    ));
 4202    let language_with_pound_comments = Arc::new(Language::new(
 4203        LanguageConfig {
 4204            line_comments: vec!["# ".into()],
 4205            ..LanguageConfig::default()
 4206        },
 4207        None,
 4208    ));
 4209    let markdown_language = Arc::new(Language::new(
 4210        LanguageConfig {
 4211            name: "Markdown".into(),
 4212            ..LanguageConfig::default()
 4213        },
 4214        None,
 4215    ));
 4216    let language_with_doc_comments = Arc::new(Language::new(
 4217        LanguageConfig {
 4218            line_comments: vec!["// ".into(), "/// ".into()],
 4219            ..LanguageConfig::default()
 4220        },
 4221        Some(tree_sitter_rust::LANGUAGE.into()),
 4222    ));
 4223
 4224    let plaintext_language = Arc::new(Language::new(
 4225        LanguageConfig {
 4226            name: "Plain Text".into(),
 4227            ..LanguageConfig::default()
 4228        },
 4229        None,
 4230    ));
 4231
 4232    assert_rewrap(
 4233        indoc! {"
 4234            // ˇ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.
 4235        "},
 4236        indoc! {"
 4237            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4238            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4239            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4240            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4241            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4242            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4243            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4244            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4245            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4246            // porttitor id. Aliquam id accumsan eros.
 4247        "},
 4248        language_with_c_comments.clone(),
 4249        &mut cx,
 4250    );
 4251
 4252    // Test that rewrapping works inside of a selection
 4253    assert_rewrap(
 4254        indoc! {"
 4255            «// 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.ˇ»
 4256        "},
 4257        indoc! {"
 4258            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4259            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4260            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4261            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4262            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4263            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4264            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4265            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4266            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4267            // porttitor id. Aliquam id accumsan eros.ˇ»
 4268        "},
 4269        language_with_c_comments.clone(),
 4270        &mut cx,
 4271    );
 4272
 4273    // Test that cursors that expand to the same region are collapsed.
 4274    assert_rewrap(
 4275        indoc! {"
 4276            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4277            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4278            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4279            // ˇ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.
 4280        "},
 4281        indoc! {"
 4282            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4283            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4284            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4285            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4286            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4287            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4288            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4289            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4290            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4291            // porttitor id. Aliquam id accumsan eros.
 4292        "},
 4293        language_with_c_comments.clone(),
 4294        &mut cx,
 4295    );
 4296
 4297    // Test that non-contiguous selections are treated separately.
 4298    assert_rewrap(
 4299        indoc! {"
 4300            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4301            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4302            //
 4303            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4304            // ˇ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.
 4305        "},
 4306        indoc! {"
 4307            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4308            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4309            // auctor, eu lacinia sapien scelerisque.
 4310            //
 4311            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4312            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4313            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4314            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4315            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4316            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4317            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4318        "},
 4319        language_with_c_comments.clone(),
 4320        &mut cx,
 4321    );
 4322
 4323    // Test that different comment prefixes are supported.
 4324    assert_rewrap(
 4325        indoc! {"
 4326            # ˇ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.
 4327        "},
 4328        indoc! {"
 4329            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4330            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4331            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4332            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4333            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4334            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4335            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4336            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4337            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4338            # accumsan eros.
 4339        "},
 4340        language_with_pound_comments.clone(),
 4341        &mut cx,
 4342    );
 4343
 4344    // Test that rewrapping is ignored outside of comments in most languages.
 4345    assert_rewrap(
 4346        indoc! {"
 4347            /// Adds two numbers.
 4348            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4349            fn add(a: u32, b: u32) -> u32 {
 4350                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ˇ
 4351            }
 4352        "},
 4353        indoc! {"
 4354            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4355            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4356            fn add(a: u32, b: u32) -> u32 {
 4357                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ˇ
 4358            }
 4359        "},
 4360        language_with_doc_comments.clone(),
 4361        &mut cx,
 4362    );
 4363
 4364    // Test that rewrapping works in Markdown and Plain Text languages.
 4365    assert_rewrap(
 4366        indoc! {"
 4367            # Hello
 4368
 4369            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.
 4370        "},
 4371        indoc! {"
 4372            # Hello
 4373
 4374            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4375            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4376            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4377            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4378            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4379            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4380            Integer sit amet scelerisque nisi.
 4381        "},
 4382        markdown_language,
 4383        &mut cx,
 4384    );
 4385
 4386    assert_rewrap(
 4387        indoc! {"
 4388            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.
 4389        "},
 4390        indoc! {"
 4391            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4392            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4393            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4394            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4395            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4396            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4397            Integer sit amet scelerisque nisi.
 4398        "},
 4399        plaintext_language,
 4400        &mut cx,
 4401    );
 4402
 4403    // Test rewrapping unaligned comments in a selection.
 4404    assert_rewrap(
 4405        indoc! {"
 4406            fn foo() {
 4407                if true {
 4408            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4409            // Praesent semper egestas tellus id dignissim.ˇ»
 4410                    do_something();
 4411                } else {
 4412                    //
 4413                }
 4414            }
 4415        "},
 4416        indoc! {"
 4417            fn foo() {
 4418                if true {
 4419            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4420                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4421                    // egestas tellus id dignissim.ˇ»
 4422                    do_something();
 4423                } else {
 4424                    //
 4425                }
 4426            }
 4427        "},
 4428        language_with_doc_comments.clone(),
 4429        &mut cx,
 4430    );
 4431
 4432    assert_rewrap(
 4433        indoc! {"
 4434            fn foo() {
 4435                if true {
 4436            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4437            // Praesent semper egestas tellus id dignissim.»
 4438                    do_something();
 4439                } else {
 4440                    //
 4441                }
 4442
 4443            }
 4444        "},
 4445        indoc! {"
 4446            fn foo() {
 4447                if true {
 4448            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4449                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4450                    // egestas tellus id dignissim.»
 4451                    do_something();
 4452                } else {
 4453                    //
 4454                }
 4455
 4456            }
 4457        "},
 4458        language_with_doc_comments.clone(),
 4459        &mut cx,
 4460    );
 4461
 4462    #[track_caller]
 4463    fn assert_rewrap(
 4464        unwrapped_text: &str,
 4465        wrapped_text: &str,
 4466        language: Arc<Language>,
 4467        cx: &mut EditorTestContext,
 4468    ) {
 4469        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4470        cx.set_state(unwrapped_text);
 4471        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4472        cx.assert_editor_state(wrapped_text);
 4473    }
 4474}
 4475
 4476#[gpui::test]
 4477async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4478    init_test(cx, |_| {});
 4479
 4480    let mut cx = EditorTestContext::new(cx).await;
 4481
 4482    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4483    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4484    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4485
 4486    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4487    cx.set_state("two ˇfour ˇsix ˇ");
 4488    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4489    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4490
 4491    // Paste again but with only two cursors. Since the number of cursors doesn't
 4492    // match the number of slices in the clipboard, the entire clipboard text
 4493    // is pasted at each cursor.
 4494    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4495    cx.update_editor(|e, cx| {
 4496        e.handle_input("( ", cx);
 4497        e.paste(&Paste, cx);
 4498        e.handle_input(") ", cx);
 4499    });
 4500    cx.assert_editor_state(
 4501        &([
 4502            "( one✅ ",
 4503            "three ",
 4504            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4505            "three ",
 4506            "five ) ˇ",
 4507        ]
 4508        .join("\n")),
 4509    );
 4510
 4511    // Cut with three selections, one of which is full-line.
 4512    cx.set_state(indoc! {"
 4513        1«2ˇ»3
 4514        4ˇ567
 4515        «8ˇ»9"});
 4516    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4517    cx.assert_editor_state(indoc! {"
 4518        1ˇ3
 4519        ˇ9"});
 4520
 4521    // Paste with three selections, noticing how the copied selection that was full-line
 4522    // gets inserted before the second cursor.
 4523    cx.set_state(indoc! {"
 4524        1ˇ3
 4525 4526        «oˇ»ne"});
 4527    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4528    cx.assert_editor_state(indoc! {"
 4529        12ˇ3
 4530        4567
 4531 4532        8ˇne"});
 4533
 4534    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4535    cx.set_state(indoc! {"
 4536        The quick brown
 4537        fox juˇmps over
 4538        the lazy dog"});
 4539    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4540    assert_eq!(
 4541        cx.read_from_clipboard()
 4542            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4543        Some("fox jumps over\n".to_string())
 4544    );
 4545
 4546    // Paste with three selections, noticing how the copied full-line selection is inserted
 4547    // before the empty selections but replaces the selection that is non-empty.
 4548    cx.set_state(indoc! {"
 4549        Tˇhe quick brown
 4550        «foˇ»x jumps over
 4551        tˇhe lazy dog"});
 4552    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4553    cx.assert_editor_state(indoc! {"
 4554        fox jumps over
 4555        Tˇhe quick brown
 4556        fox jumps over
 4557        ˇx jumps over
 4558        fox jumps over
 4559        tˇhe lazy dog"});
 4560}
 4561
 4562#[gpui::test]
 4563async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4564    init_test(cx, |_| {});
 4565
 4566    let mut cx = EditorTestContext::new(cx).await;
 4567    let language = Arc::new(Language::new(
 4568        LanguageConfig::default(),
 4569        Some(tree_sitter_rust::LANGUAGE.into()),
 4570    ));
 4571    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4572
 4573    // Cut an indented block, without the leading whitespace.
 4574    cx.set_state(indoc! {"
 4575        const a: B = (
 4576            c(),
 4577            «d(
 4578                e,
 4579                f
 4580            )ˇ»
 4581        );
 4582    "});
 4583    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4584    cx.assert_editor_state(indoc! {"
 4585        const a: B = (
 4586            c(),
 4587            ˇ
 4588        );
 4589    "});
 4590
 4591    // Paste it at the same position.
 4592    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4593    cx.assert_editor_state(indoc! {"
 4594        const a: B = (
 4595            c(),
 4596            d(
 4597                e,
 4598                f
 4599 4600        );
 4601    "});
 4602
 4603    // Paste it at a line with a lower indent level.
 4604    cx.set_state(indoc! {"
 4605        ˇ
 4606        const a: B = (
 4607            c(),
 4608        );
 4609    "});
 4610    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4611    cx.assert_editor_state(indoc! {"
 4612        d(
 4613            e,
 4614            f
 4615 4616        const a: B = (
 4617            c(),
 4618        );
 4619    "});
 4620
 4621    // Cut an indented block, with the leading whitespace.
 4622    cx.set_state(indoc! {"
 4623        const a: B = (
 4624            c(),
 4625        «    d(
 4626                e,
 4627                f
 4628            )
 4629        ˇ»);
 4630    "});
 4631    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4632    cx.assert_editor_state(indoc! {"
 4633        const a: B = (
 4634            c(),
 4635        ˇ);
 4636    "});
 4637
 4638    // Paste it at the same position.
 4639    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4640    cx.assert_editor_state(indoc! {"
 4641        const a: B = (
 4642            c(),
 4643            d(
 4644                e,
 4645                f
 4646            )
 4647        ˇ);
 4648    "});
 4649
 4650    // Paste it at a line with a higher indent level.
 4651    cx.set_state(indoc! {"
 4652        const a: B = (
 4653            c(),
 4654            d(
 4655                e,
 4656 4657            )
 4658        );
 4659    "});
 4660    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4661    cx.assert_editor_state(indoc! {"
 4662        const a: B = (
 4663            c(),
 4664            d(
 4665                e,
 4666                f    d(
 4667                    e,
 4668                    f
 4669                )
 4670        ˇ
 4671            )
 4672        );
 4673    "});
 4674}
 4675
 4676#[gpui::test]
 4677fn test_select_all(cx: &mut TestAppContext) {
 4678    init_test(cx, |_| {});
 4679
 4680    let view = cx.add_window(|cx| {
 4681        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4682        build_editor(buffer, cx)
 4683    });
 4684    _ = view.update(cx, |view, cx| {
 4685        view.select_all(&SelectAll, cx);
 4686        assert_eq!(
 4687            view.selections.display_ranges(cx),
 4688            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4689        );
 4690    });
 4691}
 4692
 4693#[gpui::test]
 4694fn test_select_line(cx: &mut TestAppContext) {
 4695    init_test(cx, |_| {});
 4696
 4697    let view = cx.add_window(|cx| {
 4698        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4699        build_editor(buffer, cx)
 4700    });
 4701    _ = view.update(cx, |view, cx| {
 4702        view.change_selections(None, cx, |s| {
 4703            s.select_display_ranges([
 4704                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4705                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4706                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4707                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4708            ])
 4709        });
 4710        view.select_line(&SelectLine, cx);
 4711        assert_eq!(
 4712            view.selections.display_ranges(cx),
 4713            vec![
 4714                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4715                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4716            ]
 4717        );
 4718    });
 4719
 4720    _ = view.update(cx, |view, cx| {
 4721        view.select_line(&SelectLine, cx);
 4722        assert_eq!(
 4723            view.selections.display_ranges(cx),
 4724            vec![
 4725                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4726                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4727            ]
 4728        );
 4729    });
 4730
 4731    _ = view.update(cx, |view, cx| {
 4732        view.select_line(&SelectLine, cx);
 4733        assert_eq!(
 4734            view.selections.display_ranges(cx),
 4735            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4736        );
 4737    });
 4738}
 4739
 4740#[gpui::test]
 4741fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4742    init_test(cx, |_| {});
 4743
 4744    let view = cx.add_window(|cx| {
 4745        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4746        build_editor(buffer, cx)
 4747    });
 4748    _ = view.update(cx, |view, cx| {
 4749        view.fold_creases(
 4750            vec![
 4751                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4752                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4753                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4754            ],
 4755            true,
 4756            cx,
 4757        );
 4758        view.change_selections(None, cx, |s| {
 4759            s.select_display_ranges([
 4760                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4761                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4762                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4763                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4764            ])
 4765        });
 4766        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4767    });
 4768
 4769    _ = view.update(cx, |view, cx| {
 4770        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4771        assert_eq!(
 4772            view.display_text(cx),
 4773            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4774        );
 4775        assert_eq!(
 4776            view.selections.display_ranges(cx),
 4777            [
 4778                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4779                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4780                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4781                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4782            ]
 4783        );
 4784    });
 4785
 4786    _ = view.update(cx, |view, cx| {
 4787        view.change_selections(None, cx, |s| {
 4788            s.select_display_ranges([
 4789                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4790            ])
 4791        });
 4792        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4793        assert_eq!(
 4794            view.display_text(cx),
 4795            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4796        );
 4797        assert_eq!(
 4798            view.selections.display_ranges(cx),
 4799            [
 4800                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4801                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4802                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4803                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4804                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4805                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4806                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4807                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4808            ]
 4809        );
 4810    });
 4811}
 4812
 4813#[gpui::test]
 4814async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4815    init_test(cx, |_| {});
 4816
 4817    let mut cx = EditorTestContext::new(cx).await;
 4818
 4819    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4820    cx.set_state(indoc!(
 4821        r#"abc
 4822           defˇghi
 4823
 4824           jk
 4825           nlmo
 4826           "#
 4827    ));
 4828
 4829    cx.update_editor(|editor, cx| {
 4830        editor.add_selection_above(&Default::default(), cx);
 4831    });
 4832
 4833    cx.assert_editor_state(indoc!(
 4834        r#"abcˇ
 4835           defˇghi
 4836
 4837           jk
 4838           nlmo
 4839           "#
 4840    ));
 4841
 4842    cx.update_editor(|editor, cx| {
 4843        editor.add_selection_above(&Default::default(), cx);
 4844    });
 4845
 4846    cx.assert_editor_state(indoc!(
 4847        r#"abcˇ
 4848            defˇghi
 4849
 4850            jk
 4851            nlmo
 4852            "#
 4853    ));
 4854
 4855    cx.update_editor(|view, cx| {
 4856        view.add_selection_below(&Default::default(), cx);
 4857    });
 4858
 4859    cx.assert_editor_state(indoc!(
 4860        r#"abc
 4861           defˇghi
 4862
 4863           jk
 4864           nlmo
 4865           "#
 4866    ));
 4867
 4868    cx.update_editor(|view, cx| {
 4869        view.undo_selection(&Default::default(), cx);
 4870    });
 4871
 4872    cx.assert_editor_state(indoc!(
 4873        r#"abcˇ
 4874           defˇghi
 4875
 4876           jk
 4877           nlmo
 4878           "#
 4879    ));
 4880
 4881    cx.update_editor(|view, cx| {
 4882        view.redo_selection(&Default::default(), cx);
 4883    });
 4884
 4885    cx.assert_editor_state(indoc!(
 4886        r#"abc
 4887           defˇghi
 4888
 4889           jk
 4890           nlmo
 4891           "#
 4892    ));
 4893
 4894    cx.update_editor(|view, cx| {
 4895        view.add_selection_below(&Default::default(), cx);
 4896    });
 4897
 4898    cx.assert_editor_state(indoc!(
 4899        r#"abc
 4900           defˇghi
 4901
 4902           jk
 4903           nlmˇo
 4904           "#
 4905    ));
 4906
 4907    cx.update_editor(|view, cx| {
 4908        view.add_selection_below(&Default::default(), cx);
 4909    });
 4910
 4911    cx.assert_editor_state(indoc!(
 4912        r#"abc
 4913           defˇghi
 4914
 4915           jk
 4916           nlmˇo
 4917           "#
 4918    ));
 4919
 4920    // change selections
 4921    cx.set_state(indoc!(
 4922        r#"abc
 4923           def«ˇg»hi
 4924
 4925           jk
 4926           nlmo
 4927           "#
 4928    ));
 4929
 4930    cx.update_editor(|view, cx| {
 4931        view.add_selection_below(&Default::default(), cx);
 4932    });
 4933
 4934    cx.assert_editor_state(indoc!(
 4935        r#"abc
 4936           def«ˇg»hi
 4937
 4938           jk
 4939           nlm«ˇo»
 4940           "#
 4941    ));
 4942
 4943    cx.update_editor(|view, cx| {
 4944        view.add_selection_below(&Default::default(), cx);
 4945    });
 4946
 4947    cx.assert_editor_state(indoc!(
 4948        r#"abc
 4949           def«ˇg»hi
 4950
 4951           jk
 4952           nlm«ˇo»
 4953           "#
 4954    ));
 4955
 4956    cx.update_editor(|view, cx| {
 4957        view.add_selection_above(&Default::default(), cx);
 4958    });
 4959
 4960    cx.assert_editor_state(indoc!(
 4961        r#"abc
 4962           def«ˇg»hi
 4963
 4964           jk
 4965           nlmo
 4966           "#
 4967    ));
 4968
 4969    cx.update_editor(|view, cx| {
 4970        view.add_selection_above(&Default::default(), cx);
 4971    });
 4972
 4973    cx.assert_editor_state(indoc!(
 4974        r#"abc
 4975           def«ˇg»hi
 4976
 4977           jk
 4978           nlmo
 4979           "#
 4980    ));
 4981
 4982    // Change selections again
 4983    cx.set_state(indoc!(
 4984        r#"a«bc
 4985           defgˇ»hi
 4986
 4987           jk
 4988           nlmo
 4989           "#
 4990    ));
 4991
 4992    cx.update_editor(|view, cx| {
 4993        view.add_selection_below(&Default::default(), cx);
 4994    });
 4995
 4996    cx.assert_editor_state(indoc!(
 4997        r#"a«bcˇ»
 4998           d«efgˇ»hi
 4999
 5000           j«kˇ»
 5001           nlmo
 5002           "#
 5003    ));
 5004
 5005    cx.update_editor(|view, cx| {
 5006        view.add_selection_below(&Default::default(), cx);
 5007    });
 5008    cx.assert_editor_state(indoc!(
 5009        r#"a«bcˇ»
 5010           d«efgˇ»hi
 5011
 5012           j«kˇ»
 5013           n«lmoˇ»
 5014           "#
 5015    ));
 5016    cx.update_editor(|view, cx| {
 5017        view.add_selection_above(&Default::default(), cx);
 5018    });
 5019
 5020    cx.assert_editor_state(indoc!(
 5021        r#"a«bcˇ»
 5022           d«efgˇ»hi
 5023
 5024           j«kˇ»
 5025           nlmo
 5026           "#
 5027    ));
 5028
 5029    // Change selections again
 5030    cx.set_state(indoc!(
 5031        r#"abc
 5032           d«ˇefghi
 5033
 5034           jk
 5035           nlm»o
 5036           "#
 5037    ));
 5038
 5039    cx.update_editor(|view, cx| {
 5040        view.add_selection_above(&Default::default(), cx);
 5041    });
 5042
 5043    cx.assert_editor_state(indoc!(
 5044        r#"a«ˇbc»
 5045           d«ˇef»ghi
 5046
 5047           j«ˇk»
 5048           n«ˇlm»o
 5049           "#
 5050    ));
 5051
 5052    cx.update_editor(|view, cx| {
 5053        view.add_selection_below(&Default::default(), cx);
 5054    });
 5055
 5056    cx.assert_editor_state(indoc!(
 5057        r#"abc
 5058           d«ˇef»ghi
 5059
 5060           j«ˇk»
 5061           n«ˇlm»o
 5062           "#
 5063    ));
 5064}
 5065
 5066#[gpui::test]
 5067async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5068    init_test(cx, |_| {});
 5069
 5070    let mut cx = EditorTestContext::new(cx).await;
 5071    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5072
 5073    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5074        .unwrap();
 5075    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5076
 5077    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5078        .unwrap();
 5079    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5080
 5081    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5082    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5083
 5084    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5085    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5086
 5087    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5088        .unwrap();
 5089    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5090
 5091    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5092        .unwrap();
 5093    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5094}
 5095
 5096#[gpui::test]
 5097async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5098    init_test(cx, |_| {});
 5099
 5100    let mut cx = EditorTestContext::new(cx).await;
 5101    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5102
 5103    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5104        .unwrap();
 5105    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5106}
 5107
 5108#[gpui::test]
 5109async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5110    init_test(cx, |_| {});
 5111
 5112    let mut cx = EditorTestContext::new(cx).await;
 5113    cx.set_state(
 5114        r#"let foo = 2;
 5115lˇet foo = 2;
 5116let fooˇ = 2;
 5117let foo = 2;
 5118let foo = ˇ2;"#,
 5119    );
 5120
 5121    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5122        .unwrap();
 5123    cx.assert_editor_state(
 5124        r#"let foo = 2;
 5125«letˇ» foo = 2;
 5126let «fooˇ» = 2;
 5127let foo = 2;
 5128let foo = «2ˇ»;"#,
 5129    );
 5130
 5131    // noop for multiple selections with different contents
 5132    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5133        .unwrap();
 5134    cx.assert_editor_state(
 5135        r#"let foo = 2;
 5136«letˇ» foo = 2;
 5137let «fooˇ» = 2;
 5138let foo = 2;
 5139let foo = «2ˇ»;"#,
 5140    );
 5141}
 5142
 5143#[gpui::test]
 5144async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5145    init_test(cx, |_| {});
 5146
 5147    let mut cx =
 5148        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5149
 5150    cx.assert_editor_state(indoc! {"
 5151        ˇbbb
 5152        ccc
 5153
 5154        bbb
 5155        ccc
 5156        "});
 5157    cx.dispatch_action(SelectPrevious::default());
 5158    cx.assert_editor_state(indoc! {"
 5159                «bbbˇ»
 5160                ccc
 5161
 5162                bbb
 5163                ccc
 5164                "});
 5165    cx.dispatch_action(SelectPrevious::default());
 5166    cx.assert_editor_state(indoc! {"
 5167                «bbbˇ»
 5168                ccc
 5169
 5170                «bbbˇ»
 5171                ccc
 5172                "});
 5173}
 5174
 5175#[gpui::test]
 5176async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5177    init_test(cx, |_| {});
 5178
 5179    let mut cx = EditorTestContext::new(cx).await;
 5180    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5181
 5182    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5183        .unwrap();
 5184    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5185
 5186    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5187        .unwrap();
 5188    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5189
 5190    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5191    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5192
 5193    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5194    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5195
 5196    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5197        .unwrap();
 5198    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5199
 5200    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5201        .unwrap();
 5202    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5203
 5204    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5205        .unwrap();
 5206    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5207}
 5208
 5209#[gpui::test]
 5210async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5211    init_test(cx, |_| {});
 5212
 5213    let mut cx = EditorTestContext::new(cx).await;
 5214    cx.set_state(
 5215        r#"let foo = 2;
 5216lˇet foo = 2;
 5217let fooˇ = 2;
 5218let foo = 2;
 5219let foo = ˇ2;"#,
 5220    );
 5221
 5222    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5223        .unwrap();
 5224    cx.assert_editor_state(
 5225        r#"let foo = 2;
 5226«letˇ» foo = 2;
 5227let «fooˇ» = 2;
 5228let foo = 2;
 5229let foo = «2ˇ»;"#,
 5230    );
 5231
 5232    // noop for multiple selections with different contents
 5233    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5234        .unwrap();
 5235    cx.assert_editor_state(
 5236        r#"let foo = 2;
 5237«letˇ» foo = 2;
 5238let «fooˇ» = 2;
 5239let foo = 2;
 5240let foo = «2ˇ»;"#,
 5241    );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5250
 5251    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5252        .unwrap();
 5253    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5254
 5255    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5256        .unwrap();
 5257    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5258
 5259    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5260    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5261
 5262    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5263    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5264
 5265    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5266        .unwrap();
 5267    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5268
 5269    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5270        .unwrap();
 5271    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5272}
 5273
 5274#[gpui::test]
 5275async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5276    init_test(cx, |_| {});
 5277
 5278    let language = Arc::new(Language::new(
 5279        LanguageConfig::default(),
 5280        Some(tree_sitter_rust::LANGUAGE.into()),
 5281    ));
 5282
 5283    let text = r#"
 5284        use mod1::mod2::{mod3, mod4};
 5285
 5286        fn fn_1(param1: bool, param2: &str) {
 5287            let var1 = "text";
 5288        }
 5289    "#
 5290    .unindent();
 5291
 5292    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5293    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5294    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5295
 5296    editor
 5297        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5298        .await;
 5299
 5300    editor.update(cx, |view, cx| {
 5301        view.change_selections(None, cx, |s| {
 5302            s.select_display_ranges([
 5303                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5304                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5305                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5306            ]);
 5307        });
 5308        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5309    });
 5310    editor.update(cx, |editor, cx| {
 5311        assert_text_with_selections(
 5312            editor,
 5313            indoc! {r#"
 5314                use mod1::mod2::{mod3, «mod4ˇ»};
 5315
 5316                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5317                    let var1 = "«textˇ»";
 5318                }
 5319            "#},
 5320            cx,
 5321        );
 5322    });
 5323
 5324    editor.update(cx, |view, cx| {
 5325        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5326    });
 5327    editor.update(cx, |editor, cx| {
 5328        assert_text_with_selections(
 5329            editor,
 5330            indoc! {r#"
 5331                use mod1::mod2::«{mod3, mod4}ˇ»;
 5332
 5333                «ˇfn fn_1(param1: bool, param2: &str) {
 5334                    let var1 = "text";
 5335 5336            "#},
 5337            cx,
 5338        );
 5339    });
 5340
 5341    editor.update(cx, |view, cx| {
 5342        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5343    });
 5344    assert_eq!(
 5345        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5346        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5347    );
 5348
 5349    // Trying to expand the selected syntax node one more time has no effect.
 5350    editor.update(cx, |view, cx| {
 5351        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5352    });
 5353    assert_eq!(
 5354        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5355        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5356    );
 5357
 5358    editor.update(cx, |view, cx| {
 5359        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5360    });
 5361    editor.update(cx, |editor, cx| {
 5362        assert_text_with_selections(
 5363            editor,
 5364            indoc! {r#"
 5365                use mod1::mod2::«{mod3, mod4}ˇ»;
 5366
 5367                «ˇfn fn_1(param1: bool, param2: &str) {
 5368                    let var1 = "text";
 5369 5370            "#},
 5371            cx,
 5372        );
 5373    });
 5374
 5375    editor.update(cx, |view, cx| {
 5376        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5377    });
 5378    editor.update(cx, |editor, cx| {
 5379        assert_text_with_selections(
 5380            editor,
 5381            indoc! {r#"
 5382                use mod1::mod2::{mod3, «mod4ˇ»};
 5383
 5384                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5385                    let var1 = "«textˇ»";
 5386                }
 5387            "#},
 5388            cx,
 5389        );
 5390    });
 5391
 5392    editor.update(cx, |view, cx| {
 5393        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5394    });
 5395    editor.update(cx, |editor, cx| {
 5396        assert_text_with_selections(
 5397            editor,
 5398            indoc! {r#"
 5399                use mod1::mod2::{mod3, mo«ˇ»d4};
 5400
 5401                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5402                    let var1 = "te«ˇ»xt";
 5403                }
 5404            "#},
 5405            cx,
 5406        );
 5407    });
 5408
 5409    // Trying to shrink the selected syntax node one more time has no effect.
 5410    editor.update(cx, |view, cx| {
 5411        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5412    });
 5413    editor.update(cx, |editor, cx| {
 5414        assert_text_with_selections(
 5415            editor,
 5416            indoc! {r#"
 5417                use mod1::mod2::{mod3, mo«ˇ»d4};
 5418
 5419                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5420                    let var1 = "te«ˇ»xt";
 5421                }
 5422            "#},
 5423            cx,
 5424        );
 5425    });
 5426
 5427    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5428    // a fold.
 5429    editor.update(cx, |view, cx| {
 5430        view.fold_creases(
 5431            vec![
 5432                Crease::simple(
 5433                    Point::new(0, 21)..Point::new(0, 24),
 5434                    FoldPlaceholder::test(),
 5435                ),
 5436                Crease::simple(
 5437                    Point::new(3, 20)..Point::new(3, 22),
 5438                    FoldPlaceholder::test(),
 5439                ),
 5440            ],
 5441            true,
 5442            cx,
 5443        );
 5444        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5445    });
 5446    editor.update(cx, |editor, cx| {
 5447        assert_text_with_selections(
 5448            editor,
 5449            indoc! {r#"
 5450                use mod1::mod2::«{mod3, mod4}ˇ»;
 5451
 5452                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5453                    «let var1 = "text";ˇ»
 5454                }
 5455            "#},
 5456            cx,
 5457        );
 5458    });
 5459}
 5460
 5461#[gpui::test]
 5462async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5463    init_test(cx, |_| {});
 5464
 5465    let language = Arc::new(
 5466        Language::new(
 5467            LanguageConfig {
 5468                brackets: BracketPairConfig {
 5469                    pairs: vec![
 5470                        BracketPair {
 5471                            start: "{".to_string(),
 5472                            end: "}".to_string(),
 5473                            close: false,
 5474                            surround: false,
 5475                            newline: true,
 5476                        },
 5477                        BracketPair {
 5478                            start: "(".to_string(),
 5479                            end: ")".to_string(),
 5480                            close: false,
 5481                            surround: false,
 5482                            newline: true,
 5483                        },
 5484                    ],
 5485                    ..Default::default()
 5486                },
 5487                ..Default::default()
 5488            },
 5489            Some(tree_sitter_rust::LANGUAGE.into()),
 5490        )
 5491        .with_indents_query(
 5492            r#"
 5493                (_ "(" ")" @end) @indent
 5494                (_ "{" "}" @end) @indent
 5495            "#,
 5496        )
 5497        .unwrap(),
 5498    );
 5499
 5500    let text = "fn a() {}";
 5501
 5502    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5503    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5504    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5505    editor
 5506        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5507        .await;
 5508
 5509    editor.update(cx, |editor, cx| {
 5510        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5511        editor.newline(&Newline, cx);
 5512        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5513        assert_eq!(
 5514            editor.selections.ranges(cx),
 5515            &[
 5516                Point::new(1, 4)..Point::new(1, 4),
 5517                Point::new(3, 4)..Point::new(3, 4),
 5518                Point::new(5, 0)..Point::new(5, 0)
 5519            ]
 5520        );
 5521    });
 5522}
 5523
 5524#[gpui::test]
 5525async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5526    init_test(cx, |_| {});
 5527
 5528    {
 5529        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5530        cx.set_state(indoc! {"
 5531            impl A {
 5532
 5533                fn b() {}
 5534
 5535            «fn c() {
 5536
 5537            }ˇ»
 5538            }
 5539        "});
 5540
 5541        cx.update_editor(|editor, cx| {
 5542            editor.autoindent(&Default::default(), cx);
 5543        });
 5544
 5545        cx.assert_editor_state(indoc! {"
 5546            impl A {
 5547
 5548                fn b() {}
 5549
 5550                «fn c() {
 5551
 5552                }ˇ»
 5553            }
 5554        "});
 5555    }
 5556
 5557    {
 5558        let mut cx = EditorTestContext::new_multibuffer(
 5559            cx,
 5560            [indoc! { "
 5561                impl A {
 5562                «
 5563                // a
 5564                fn b(){}
 5565                »
 5566                «
 5567                    }
 5568                    fn c(){}
 5569                »
 5570            "}],
 5571        );
 5572
 5573        let buffer = cx.update_editor(|editor, cx| {
 5574            let buffer = editor.buffer().update(cx, |buffer, _| {
 5575                buffer.all_buffers().iter().next().unwrap().clone()
 5576            });
 5577            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5578            buffer
 5579        });
 5580
 5581        cx.run_until_parked();
 5582        cx.update_editor(|editor, cx| {
 5583            editor.select_all(&Default::default(), cx);
 5584            editor.autoindent(&Default::default(), cx)
 5585        });
 5586        cx.run_until_parked();
 5587
 5588        cx.update(|cx| {
 5589            pretty_assertions::assert_eq!(
 5590                buffer.read(cx).text(),
 5591                indoc! { "
 5592                    impl A {
 5593
 5594                        // a
 5595                        fn b(){}
 5596
 5597
 5598                    }
 5599                    fn c(){}
 5600
 5601                " }
 5602            )
 5603        });
 5604    }
 5605}
 5606
 5607#[gpui::test]
 5608async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5609    init_test(cx, |_| {});
 5610
 5611    let mut cx = EditorTestContext::new(cx).await;
 5612
 5613    let language = Arc::new(Language::new(
 5614        LanguageConfig {
 5615            brackets: BracketPairConfig {
 5616                pairs: vec![
 5617                    BracketPair {
 5618                        start: "{".to_string(),
 5619                        end: "}".to_string(),
 5620                        close: true,
 5621                        surround: true,
 5622                        newline: true,
 5623                    },
 5624                    BracketPair {
 5625                        start: "(".to_string(),
 5626                        end: ")".to_string(),
 5627                        close: true,
 5628                        surround: true,
 5629                        newline: true,
 5630                    },
 5631                    BracketPair {
 5632                        start: "/*".to_string(),
 5633                        end: " */".to_string(),
 5634                        close: true,
 5635                        surround: true,
 5636                        newline: true,
 5637                    },
 5638                    BracketPair {
 5639                        start: "[".to_string(),
 5640                        end: "]".to_string(),
 5641                        close: false,
 5642                        surround: false,
 5643                        newline: true,
 5644                    },
 5645                    BracketPair {
 5646                        start: "\"".to_string(),
 5647                        end: "\"".to_string(),
 5648                        close: true,
 5649                        surround: true,
 5650                        newline: false,
 5651                    },
 5652                    BracketPair {
 5653                        start: "<".to_string(),
 5654                        end: ">".to_string(),
 5655                        close: false,
 5656                        surround: true,
 5657                        newline: true,
 5658                    },
 5659                ],
 5660                ..Default::default()
 5661            },
 5662            autoclose_before: "})]".to_string(),
 5663            ..Default::default()
 5664        },
 5665        Some(tree_sitter_rust::LANGUAGE.into()),
 5666    ));
 5667
 5668    cx.language_registry().add(language.clone());
 5669    cx.update_buffer(|buffer, cx| {
 5670        buffer.set_language(Some(language), cx);
 5671    });
 5672
 5673    cx.set_state(
 5674        &r#"
 5675            🏀ˇ
 5676            εˇ
 5677            ❤️ˇ
 5678        "#
 5679        .unindent(),
 5680    );
 5681
 5682    // autoclose multiple nested brackets at multiple cursors
 5683    cx.update_editor(|view, cx| {
 5684        view.handle_input("{", cx);
 5685        view.handle_input("{", cx);
 5686        view.handle_input("{", cx);
 5687    });
 5688    cx.assert_editor_state(
 5689        &"
 5690            🏀{{{ˇ}}}
 5691            ε{{{ˇ}}}
 5692            ❤️{{{ˇ}}}
 5693        "
 5694        .unindent(),
 5695    );
 5696
 5697    // insert a different closing bracket
 5698    cx.update_editor(|view, cx| {
 5699        view.handle_input(")", cx);
 5700    });
 5701    cx.assert_editor_state(
 5702        &"
 5703            🏀{{{)ˇ}}}
 5704            ε{{{)ˇ}}}
 5705            ❤️{{{)ˇ}}}
 5706        "
 5707        .unindent(),
 5708    );
 5709
 5710    // skip over the auto-closed brackets when typing a closing bracket
 5711    cx.update_editor(|view, cx| {
 5712        view.move_right(&MoveRight, cx);
 5713        view.handle_input("}", cx);
 5714        view.handle_input("}", cx);
 5715        view.handle_input("}", cx);
 5716    });
 5717    cx.assert_editor_state(
 5718        &"
 5719            🏀{{{)}}}}ˇ
 5720            ε{{{)}}}}ˇ
 5721            ❤️{{{)}}}}ˇ
 5722        "
 5723        .unindent(),
 5724    );
 5725
 5726    // autoclose multi-character pairs
 5727    cx.set_state(
 5728        &"
 5729            ˇ
 5730            ˇ
 5731        "
 5732        .unindent(),
 5733    );
 5734    cx.update_editor(|view, cx| {
 5735        view.handle_input("/", cx);
 5736        view.handle_input("*", cx);
 5737    });
 5738    cx.assert_editor_state(
 5739        &"
 5740            /*ˇ */
 5741            /*ˇ */
 5742        "
 5743        .unindent(),
 5744    );
 5745
 5746    // one cursor autocloses a multi-character pair, one cursor
 5747    // does not autoclose.
 5748    cx.set_state(
 5749        &"
 5750 5751            ˇ
 5752        "
 5753        .unindent(),
 5754    );
 5755    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5756    cx.assert_editor_state(
 5757        &"
 5758            /*ˇ */
 5759 5760        "
 5761        .unindent(),
 5762    );
 5763
 5764    // Don't autoclose if the next character isn't whitespace and isn't
 5765    // listed in the language's "autoclose_before" section.
 5766    cx.set_state("ˇa b");
 5767    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5768    cx.assert_editor_state("{ˇa b");
 5769
 5770    // Don't autoclose if `close` is false for the bracket pair
 5771    cx.set_state("ˇ");
 5772    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5773    cx.assert_editor_state("");
 5774
 5775    // Surround with brackets if text is selected
 5776    cx.set_state("«aˇ» b");
 5777    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5778    cx.assert_editor_state("{«aˇ»} b");
 5779
 5780    // Autclose pair where the start and end characters are the same
 5781    cx.set_state("");
 5782    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5783    cx.assert_editor_state("a\"ˇ\"");
 5784    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5785    cx.assert_editor_state("a\"\"ˇ");
 5786
 5787    // Don't autoclose pair if autoclose is disabled
 5788    cx.set_state("ˇ");
 5789    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5790    cx.assert_editor_state("");
 5791
 5792    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5793    cx.set_state("«aˇ» b");
 5794    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5795    cx.assert_editor_state("<«aˇ»> b");
 5796}
 5797
 5798#[gpui::test]
 5799async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5800    init_test(cx, |settings| {
 5801        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5802    });
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805
 5806    let language = Arc::new(Language::new(
 5807        LanguageConfig {
 5808            brackets: BracketPairConfig {
 5809                pairs: vec![
 5810                    BracketPair {
 5811                        start: "{".to_string(),
 5812                        end: "}".to_string(),
 5813                        close: true,
 5814                        surround: true,
 5815                        newline: true,
 5816                    },
 5817                    BracketPair {
 5818                        start: "(".to_string(),
 5819                        end: ")".to_string(),
 5820                        close: true,
 5821                        surround: true,
 5822                        newline: true,
 5823                    },
 5824                    BracketPair {
 5825                        start: "[".to_string(),
 5826                        end: "]".to_string(),
 5827                        close: false,
 5828                        surround: false,
 5829                        newline: true,
 5830                    },
 5831                ],
 5832                ..Default::default()
 5833            },
 5834            autoclose_before: "})]".to_string(),
 5835            ..Default::default()
 5836        },
 5837        Some(tree_sitter_rust::LANGUAGE.into()),
 5838    ));
 5839
 5840    cx.language_registry().add(language.clone());
 5841    cx.update_buffer(|buffer, cx| {
 5842        buffer.set_language(Some(language), cx);
 5843    });
 5844
 5845    cx.set_state(
 5846        &"
 5847            ˇ
 5848            ˇ
 5849            ˇ
 5850        "
 5851        .unindent(),
 5852    );
 5853
 5854    // ensure only matching closing brackets are skipped over
 5855    cx.update_editor(|view, cx| {
 5856        view.handle_input("}", cx);
 5857        view.move_left(&MoveLeft, cx);
 5858        view.handle_input(")", cx);
 5859        view.move_left(&MoveLeft, cx);
 5860    });
 5861    cx.assert_editor_state(
 5862        &"
 5863            ˇ)}
 5864            ˇ)}
 5865            ˇ)}
 5866        "
 5867        .unindent(),
 5868    );
 5869
 5870    // skip-over closing brackets at multiple cursors
 5871    cx.update_editor(|view, cx| {
 5872        view.handle_input(")", cx);
 5873        view.handle_input("}", cx);
 5874    });
 5875    cx.assert_editor_state(
 5876        &"
 5877            )}ˇ
 5878            )}ˇ
 5879            )}ˇ
 5880        "
 5881        .unindent(),
 5882    );
 5883
 5884    // ignore non-close brackets
 5885    cx.update_editor(|view, cx| {
 5886        view.handle_input("]", cx);
 5887        view.move_left(&MoveLeft, cx);
 5888        view.handle_input("]", cx);
 5889    });
 5890    cx.assert_editor_state(
 5891        &"
 5892            )}]ˇ]
 5893            )}]ˇ]
 5894            )}]ˇ]
 5895        "
 5896        .unindent(),
 5897    );
 5898}
 5899
 5900#[gpui::test]
 5901async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5902    init_test(cx, |_| {});
 5903
 5904    let mut cx = EditorTestContext::new(cx).await;
 5905
 5906    let html_language = Arc::new(
 5907        Language::new(
 5908            LanguageConfig {
 5909                name: "HTML".into(),
 5910                brackets: BracketPairConfig {
 5911                    pairs: vec![
 5912                        BracketPair {
 5913                            start: "<".into(),
 5914                            end: ">".into(),
 5915                            close: true,
 5916                            ..Default::default()
 5917                        },
 5918                        BracketPair {
 5919                            start: "{".into(),
 5920                            end: "}".into(),
 5921                            close: true,
 5922                            ..Default::default()
 5923                        },
 5924                        BracketPair {
 5925                            start: "(".into(),
 5926                            end: ")".into(),
 5927                            close: true,
 5928                            ..Default::default()
 5929                        },
 5930                    ],
 5931                    ..Default::default()
 5932                },
 5933                autoclose_before: "})]>".into(),
 5934                ..Default::default()
 5935            },
 5936            Some(tree_sitter_html::language()),
 5937        )
 5938        .with_injection_query(
 5939            r#"
 5940            (script_element
 5941                (raw_text) @content
 5942                (#set! "language" "javascript"))
 5943            "#,
 5944        )
 5945        .unwrap(),
 5946    );
 5947
 5948    let javascript_language = Arc::new(Language::new(
 5949        LanguageConfig {
 5950            name: "JavaScript".into(),
 5951            brackets: BracketPairConfig {
 5952                pairs: vec![
 5953                    BracketPair {
 5954                        start: "/*".into(),
 5955                        end: " */".into(),
 5956                        close: true,
 5957                        ..Default::default()
 5958                    },
 5959                    BracketPair {
 5960                        start: "{".into(),
 5961                        end: "}".into(),
 5962                        close: true,
 5963                        ..Default::default()
 5964                    },
 5965                    BracketPair {
 5966                        start: "(".into(),
 5967                        end: ")".into(),
 5968                        close: true,
 5969                        ..Default::default()
 5970                    },
 5971                ],
 5972                ..Default::default()
 5973            },
 5974            autoclose_before: "})]>".into(),
 5975            ..Default::default()
 5976        },
 5977        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5978    ));
 5979
 5980    cx.language_registry().add(html_language.clone());
 5981    cx.language_registry().add(javascript_language.clone());
 5982
 5983    cx.update_buffer(|buffer, cx| {
 5984        buffer.set_language(Some(html_language), cx);
 5985    });
 5986
 5987    cx.set_state(
 5988        &r#"
 5989            <body>ˇ
 5990                <script>
 5991                    var x = 1;ˇ
 5992                </script>
 5993            </body>ˇ
 5994        "#
 5995        .unindent(),
 5996    );
 5997
 5998    // Precondition: different languages are active at different locations.
 5999    cx.update_editor(|editor, cx| {
 6000        let snapshot = editor.snapshot(cx);
 6001        let cursors = editor.selections.ranges::<usize>(cx);
 6002        let languages = cursors
 6003            .iter()
 6004            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6005            .collect::<Vec<_>>();
 6006        assert_eq!(
 6007            languages,
 6008            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6009        );
 6010    });
 6011
 6012    // Angle brackets autoclose in HTML, but not JavaScript.
 6013    cx.update_editor(|editor, cx| {
 6014        editor.handle_input("<", cx);
 6015        editor.handle_input("a", cx);
 6016    });
 6017    cx.assert_editor_state(
 6018        &r#"
 6019            <body><aˇ>
 6020                <script>
 6021                    var x = 1;<aˇ
 6022                </script>
 6023            </body><aˇ>
 6024        "#
 6025        .unindent(),
 6026    );
 6027
 6028    // Curly braces and parens autoclose in both HTML and JavaScript.
 6029    cx.update_editor(|editor, cx| {
 6030        editor.handle_input(" b=", cx);
 6031        editor.handle_input("{", cx);
 6032        editor.handle_input("c", cx);
 6033        editor.handle_input("(", cx);
 6034    });
 6035    cx.assert_editor_state(
 6036        &r#"
 6037            <body><a b={c(ˇ)}>
 6038                <script>
 6039                    var x = 1;<a b={c(ˇ)}
 6040                </script>
 6041            </body><a b={c(ˇ)}>
 6042        "#
 6043        .unindent(),
 6044    );
 6045
 6046    // Brackets that were already autoclosed are skipped.
 6047    cx.update_editor(|editor, cx| {
 6048        editor.handle_input(")", cx);
 6049        editor.handle_input("d", cx);
 6050        editor.handle_input("}", cx);
 6051    });
 6052    cx.assert_editor_state(
 6053        &r#"
 6054            <body><a b={c()d}ˇ>
 6055                <script>
 6056                    var x = 1;<a b={c()d}ˇ
 6057                </script>
 6058            </body><a b={c()d}ˇ>
 6059        "#
 6060        .unindent(),
 6061    );
 6062    cx.update_editor(|editor, cx| {
 6063        editor.handle_input(">", cx);
 6064    });
 6065    cx.assert_editor_state(
 6066        &r#"
 6067            <body><a b={c()d}>ˇ
 6068                <script>
 6069                    var x = 1;<a b={c()d}>ˇ
 6070                </script>
 6071            </body><a b={c()d}>ˇ
 6072        "#
 6073        .unindent(),
 6074    );
 6075
 6076    // Reset
 6077    cx.set_state(
 6078        &r#"
 6079            <body>ˇ
 6080                <script>
 6081                    var x = 1;ˇ
 6082                </script>
 6083            </body>ˇ
 6084        "#
 6085        .unindent(),
 6086    );
 6087
 6088    cx.update_editor(|editor, cx| {
 6089        editor.handle_input("<", cx);
 6090    });
 6091    cx.assert_editor_state(
 6092        &r#"
 6093            <body><ˇ>
 6094                <script>
 6095                    var x = 1;<ˇ
 6096                </script>
 6097            </body><ˇ>
 6098        "#
 6099        .unindent(),
 6100    );
 6101
 6102    // When backspacing, the closing angle brackets are removed.
 6103    cx.update_editor(|editor, cx| {
 6104        editor.backspace(&Backspace, cx);
 6105    });
 6106    cx.assert_editor_state(
 6107        &r#"
 6108            <body>ˇ
 6109                <script>
 6110                    var x = 1;ˇ
 6111                </script>
 6112            </body>ˇ
 6113        "#
 6114        .unindent(),
 6115    );
 6116
 6117    // Block comments autoclose in JavaScript, but not HTML.
 6118    cx.update_editor(|editor, cx| {
 6119        editor.handle_input("/", cx);
 6120        editor.handle_input("*", cx);
 6121    });
 6122    cx.assert_editor_state(
 6123        &r#"
 6124            <body>/*ˇ
 6125                <script>
 6126                    var x = 1;/*ˇ */
 6127                </script>
 6128            </body>/*ˇ
 6129        "#
 6130        .unindent(),
 6131    );
 6132}
 6133
 6134#[gpui::test]
 6135async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6136    init_test(cx, |_| {});
 6137
 6138    let mut cx = EditorTestContext::new(cx).await;
 6139
 6140    let rust_language = Arc::new(
 6141        Language::new(
 6142            LanguageConfig {
 6143                name: "Rust".into(),
 6144                brackets: serde_json::from_value(json!([
 6145                    { "start": "{", "end": "}", "close": true, "newline": true },
 6146                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6147                ]))
 6148                .unwrap(),
 6149                autoclose_before: "})]>".into(),
 6150                ..Default::default()
 6151            },
 6152            Some(tree_sitter_rust::LANGUAGE.into()),
 6153        )
 6154        .with_override_query("(string_literal) @string")
 6155        .unwrap(),
 6156    );
 6157
 6158    cx.language_registry().add(rust_language.clone());
 6159    cx.update_buffer(|buffer, cx| {
 6160        buffer.set_language(Some(rust_language), cx);
 6161    });
 6162
 6163    cx.set_state(
 6164        &r#"
 6165            let x = ˇ
 6166        "#
 6167        .unindent(),
 6168    );
 6169
 6170    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6171    cx.update_editor(|editor, cx| {
 6172        editor.handle_input("\"", cx);
 6173    });
 6174    cx.assert_editor_state(
 6175        &r#"
 6176            let x = "ˇ"
 6177        "#
 6178        .unindent(),
 6179    );
 6180
 6181    // Inserting another quotation mark. The cursor moves across the existing
 6182    // automatically-inserted quotation mark.
 6183    cx.update_editor(|editor, cx| {
 6184        editor.handle_input("\"", cx);
 6185    });
 6186    cx.assert_editor_state(
 6187        &r#"
 6188            let x = ""ˇ
 6189        "#
 6190        .unindent(),
 6191    );
 6192
 6193    // Reset
 6194    cx.set_state(
 6195        &r#"
 6196            let x = ˇ
 6197        "#
 6198        .unindent(),
 6199    );
 6200
 6201    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6202    cx.update_editor(|editor, cx| {
 6203        editor.handle_input("\"", cx);
 6204        editor.handle_input(" ", cx);
 6205        editor.move_left(&Default::default(), cx);
 6206        editor.handle_input("\\", cx);
 6207        editor.handle_input("\"", cx);
 6208    });
 6209    cx.assert_editor_state(
 6210        &r#"
 6211            let x = "\"ˇ "
 6212        "#
 6213        .unindent(),
 6214    );
 6215
 6216    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6217    // mark. Nothing is inserted.
 6218    cx.update_editor(|editor, cx| {
 6219        editor.move_right(&Default::default(), cx);
 6220        editor.handle_input("\"", cx);
 6221    });
 6222    cx.assert_editor_state(
 6223        &r#"
 6224            let x = "\" "ˇ
 6225        "#
 6226        .unindent(),
 6227    );
 6228}
 6229
 6230#[gpui::test]
 6231async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6232    init_test(cx, |_| {});
 6233
 6234    let language = Arc::new(Language::new(
 6235        LanguageConfig {
 6236            brackets: BracketPairConfig {
 6237                pairs: vec![
 6238                    BracketPair {
 6239                        start: "{".to_string(),
 6240                        end: "}".to_string(),
 6241                        close: true,
 6242                        surround: true,
 6243                        newline: true,
 6244                    },
 6245                    BracketPair {
 6246                        start: "/* ".to_string(),
 6247                        end: "*/".to_string(),
 6248                        close: true,
 6249                        surround: true,
 6250                        ..Default::default()
 6251                    },
 6252                ],
 6253                ..Default::default()
 6254            },
 6255            ..Default::default()
 6256        },
 6257        Some(tree_sitter_rust::LANGUAGE.into()),
 6258    ));
 6259
 6260    let text = r#"
 6261        a
 6262        b
 6263        c
 6264    "#
 6265    .unindent();
 6266
 6267    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6268    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6269    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6270    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6271        .await;
 6272
 6273    view.update(cx, |view, cx| {
 6274        view.change_selections(None, cx, |s| {
 6275            s.select_display_ranges([
 6276                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6277                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6278                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6279            ])
 6280        });
 6281
 6282        view.handle_input("{", cx);
 6283        view.handle_input("{", cx);
 6284        view.handle_input("{", cx);
 6285        assert_eq!(
 6286            view.text(cx),
 6287            "
 6288                {{{a}}}
 6289                {{{b}}}
 6290                {{{c}}}
 6291            "
 6292            .unindent()
 6293        );
 6294        assert_eq!(
 6295            view.selections.display_ranges(cx),
 6296            [
 6297                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6298                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6299                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6300            ]
 6301        );
 6302
 6303        view.undo(&Undo, cx);
 6304        view.undo(&Undo, cx);
 6305        view.undo(&Undo, cx);
 6306        assert_eq!(
 6307            view.text(cx),
 6308            "
 6309                a
 6310                b
 6311                c
 6312            "
 6313            .unindent()
 6314        );
 6315        assert_eq!(
 6316            view.selections.display_ranges(cx),
 6317            [
 6318                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6319                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6320                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6321            ]
 6322        );
 6323
 6324        // Ensure inserting the first character of a multi-byte bracket pair
 6325        // doesn't surround the selections with the bracket.
 6326        view.handle_input("/", cx);
 6327        assert_eq!(
 6328            view.text(cx),
 6329            "
 6330                /
 6331                /
 6332                /
 6333            "
 6334            .unindent()
 6335        );
 6336        assert_eq!(
 6337            view.selections.display_ranges(cx),
 6338            [
 6339                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6340                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6341                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6342            ]
 6343        );
 6344
 6345        view.undo(&Undo, cx);
 6346        assert_eq!(
 6347            view.text(cx),
 6348            "
 6349                a
 6350                b
 6351                c
 6352            "
 6353            .unindent()
 6354        );
 6355        assert_eq!(
 6356            view.selections.display_ranges(cx),
 6357            [
 6358                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6359                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6360                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6361            ]
 6362        );
 6363
 6364        // Ensure inserting the last character of a multi-byte bracket pair
 6365        // doesn't surround the selections with the bracket.
 6366        view.handle_input("*", cx);
 6367        assert_eq!(
 6368            view.text(cx),
 6369            "
 6370                *
 6371                *
 6372                *
 6373            "
 6374            .unindent()
 6375        );
 6376        assert_eq!(
 6377            view.selections.display_ranges(cx),
 6378            [
 6379                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6380                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6381                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6382            ]
 6383        );
 6384    });
 6385}
 6386
 6387#[gpui::test]
 6388async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6389    init_test(cx, |_| {});
 6390
 6391    let language = Arc::new(Language::new(
 6392        LanguageConfig {
 6393            brackets: BracketPairConfig {
 6394                pairs: vec![BracketPair {
 6395                    start: "{".to_string(),
 6396                    end: "}".to_string(),
 6397                    close: true,
 6398                    surround: true,
 6399                    newline: true,
 6400                }],
 6401                ..Default::default()
 6402            },
 6403            autoclose_before: "}".to_string(),
 6404            ..Default::default()
 6405        },
 6406        Some(tree_sitter_rust::LANGUAGE.into()),
 6407    ));
 6408
 6409    let text = r#"
 6410        a
 6411        b
 6412        c
 6413    "#
 6414    .unindent();
 6415
 6416    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6417    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6418    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6419    editor
 6420        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6421        .await;
 6422
 6423    editor.update(cx, |editor, cx| {
 6424        editor.change_selections(None, cx, |s| {
 6425            s.select_ranges([
 6426                Point::new(0, 1)..Point::new(0, 1),
 6427                Point::new(1, 1)..Point::new(1, 1),
 6428                Point::new(2, 1)..Point::new(2, 1),
 6429            ])
 6430        });
 6431
 6432        editor.handle_input("{", cx);
 6433        editor.handle_input("{", cx);
 6434        editor.handle_input("_", cx);
 6435        assert_eq!(
 6436            editor.text(cx),
 6437            "
 6438                a{{_}}
 6439                b{{_}}
 6440                c{{_}}
 6441            "
 6442            .unindent()
 6443        );
 6444        assert_eq!(
 6445            editor.selections.ranges::<Point>(cx),
 6446            [
 6447                Point::new(0, 4)..Point::new(0, 4),
 6448                Point::new(1, 4)..Point::new(1, 4),
 6449                Point::new(2, 4)..Point::new(2, 4)
 6450            ]
 6451        );
 6452
 6453        editor.backspace(&Default::default(), cx);
 6454        editor.backspace(&Default::default(), cx);
 6455        assert_eq!(
 6456            editor.text(cx),
 6457            "
 6458                a{}
 6459                b{}
 6460                c{}
 6461            "
 6462            .unindent()
 6463        );
 6464        assert_eq!(
 6465            editor.selections.ranges::<Point>(cx),
 6466            [
 6467                Point::new(0, 2)..Point::new(0, 2),
 6468                Point::new(1, 2)..Point::new(1, 2),
 6469                Point::new(2, 2)..Point::new(2, 2)
 6470            ]
 6471        );
 6472
 6473        editor.delete_to_previous_word_start(&Default::default(), cx);
 6474        assert_eq!(
 6475            editor.text(cx),
 6476            "
 6477                a
 6478                b
 6479                c
 6480            "
 6481            .unindent()
 6482        );
 6483        assert_eq!(
 6484            editor.selections.ranges::<Point>(cx),
 6485            [
 6486                Point::new(0, 1)..Point::new(0, 1),
 6487                Point::new(1, 1)..Point::new(1, 1),
 6488                Point::new(2, 1)..Point::new(2, 1)
 6489            ]
 6490        );
 6491    });
 6492}
 6493
 6494#[gpui::test]
 6495async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6496    init_test(cx, |settings| {
 6497        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6498    });
 6499
 6500    let mut cx = EditorTestContext::new(cx).await;
 6501
 6502    let language = Arc::new(Language::new(
 6503        LanguageConfig {
 6504            brackets: BracketPairConfig {
 6505                pairs: vec![
 6506                    BracketPair {
 6507                        start: "{".to_string(),
 6508                        end: "}".to_string(),
 6509                        close: true,
 6510                        surround: true,
 6511                        newline: true,
 6512                    },
 6513                    BracketPair {
 6514                        start: "(".to_string(),
 6515                        end: ")".to_string(),
 6516                        close: true,
 6517                        surround: true,
 6518                        newline: true,
 6519                    },
 6520                    BracketPair {
 6521                        start: "[".to_string(),
 6522                        end: "]".to_string(),
 6523                        close: false,
 6524                        surround: true,
 6525                        newline: true,
 6526                    },
 6527                ],
 6528                ..Default::default()
 6529            },
 6530            autoclose_before: "})]".to_string(),
 6531            ..Default::default()
 6532        },
 6533        Some(tree_sitter_rust::LANGUAGE.into()),
 6534    ));
 6535
 6536    cx.language_registry().add(language.clone());
 6537    cx.update_buffer(|buffer, cx| {
 6538        buffer.set_language(Some(language), cx);
 6539    });
 6540
 6541    cx.set_state(
 6542        &"
 6543            {(ˇ)}
 6544            [[ˇ]]
 6545            {(ˇ)}
 6546        "
 6547        .unindent(),
 6548    );
 6549
 6550    cx.update_editor(|view, cx| {
 6551        view.backspace(&Default::default(), cx);
 6552        view.backspace(&Default::default(), cx);
 6553    });
 6554
 6555    cx.assert_editor_state(
 6556        &"
 6557            ˇ
 6558            ˇ]]
 6559            ˇ
 6560        "
 6561        .unindent(),
 6562    );
 6563
 6564    cx.update_editor(|view, cx| {
 6565        view.handle_input("{", cx);
 6566        view.handle_input("{", cx);
 6567        view.move_right(&MoveRight, cx);
 6568        view.move_right(&MoveRight, cx);
 6569        view.move_left(&MoveLeft, cx);
 6570        view.move_left(&MoveLeft, cx);
 6571        view.backspace(&Default::default(), cx);
 6572    });
 6573
 6574    cx.assert_editor_state(
 6575        &"
 6576            {ˇ}
 6577            {ˇ}]]
 6578            {ˇ}
 6579        "
 6580        .unindent(),
 6581    );
 6582
 6583    cx.update_editor(|view, cx| {
 6584        view.backspace(&Default::default(), cx);
 6585    });
 6586
 6587    cx.assert_editor_state(
 6588        &"
 6589            ˇ
 6590            ˇ]]
 6591            ˇ
 6592        "
 6593        .unindent(),
 6594    );
 6595}
 6596
 6597#[gpui::test]
 6598async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6599    init_test(cx, |_| {});
 6600
 6601    let language = Arc::new(Language::new(
 6602        LanguageConfig::default(),
 6603        Some(tree_sitter_rust::LANGUAGE.into()),
 6604    ));
 6605
 6606    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6607    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6608    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6609    editor
 6610        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6611        .await;
 6612
 6613    editor.update(cx, |editor, cx| {
 6614        editor.set_auto_replace_emoji_shortcode(true);
 6615
 6616        editor.handle_input("Hello ", cx);
 6617        editor.handle_input(":wave", cx);
 6618        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6619
 6620        editor.handle_input(":", cx);
 6621        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6622
 6623        editor.handle_input(" :smile", cx);
 6624        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6625
 6626        editor.handle_input(":", cx);
 6627        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6628
 6629        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6630        editor.handle_input(":wave", cx);
 6631        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6632
 6633        editor.handle_input(":", cx);
 6634        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6635
 6636        editor.handle_input(":1", cx);
 6637        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6638
 6639        editor.handle_input(":", cx);
 6640        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6641
 6642        // Ensure shortcode does not get replaced when it is part of a word
 6643        editor.handle_input(" Test:wave", cx);
 6644        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6645
 6646        editor.handle_input(":", cx);
 6647        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6648
 6649        editor.set_auto_replace_emoji_shortcode(false);
 6650
 6651        // Ensure shortcode does not get replaced when auto replace is off
 6652        editor.handle_input(" :wave", cx);
 6653        assert_eq!(
 6654            editor.text(cx),
 6655            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6656        );
 6657
 6658        editor.handle_input(":", cx);
 6659        assert_eq!(
 6660            editor.text(cx),
 6661            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6662        );
 6663    });
 6664}
 6665
 6666#[gpui::test]
 6667async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6668    init_test(cx, |_| {});
 6669
 6670    let (text, insertion_ranges) = marked_text_ranges(
 6671        indoc! {"
 6672            ˇ
 6673        "},
 6674        false,
 6675    );
 6676
 6677    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6678    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6679
 6680    _ = editor.update(cx, |editor, cx| {
 6681        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6682
 6683        editor
 6684            .insert_snippet(&insertion_ranges, snippet, cx)
 6685            .unwrap();
 6686
 6687        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6688            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6689            assert_eq!(editor.text(cx), expected_text);
 6690            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6691        }
 6692
 6693        assert(
 6694            editor,
 6695            cx,
 6696            indoc! {"
 6697            type «» =•
 6698            "},
 6699        );
 6700
 6701        assert!(editor.context_menu_visible(), "There should be a matches");
 6702    });
 6703}
 6704
 6705#[gpui::test]
 6706async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6707    init_test(cx, |_| {});
 6708
 6709    let (text, insertion_ranges) = marked_text_ranges(
 6710        indoc! {"
 6711            a.ˇ b
 6712            a.ˇ b
 6713            a.ˇ b
 6714        "},
 6715        false,
 6716    );
 6717
 6718    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6719    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6720
 6721    editor.update(cx, |editor, cx| {
 6722        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6723
 6724        editor
 6725            .insert_snippet(&insertion_ranges, snippet, cx)
 6726            .unwrap();
 6727
 6728        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6729            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6730            assert_eq!(editor.text(cx), expected_text);
 6731            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6732        }
 6733
 6734        assert(
 6735            editor,
 6736            cx,
 6737            indoc! {"
 6738                a.f(«one», two, «three») b
 6739                a.f(«one», two, «three») b
 6740                a.f(«one», two, «three») b
 6741            "},
 6742        );
 6743
 6744        // Can't move earlier than the first tab stop
 6745        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6746        assert(
 6747            editor,
 6748            cx,
 6749            indoc! {"
 6750                a.f(«one», two, «three») b
 6751                a.f(«one», two, «three») b
 6752                a.f(«one», two, «three») b
 6753            "},
 6754        );
 6755
 6756        assert!(editor.move_to_next_snippet_tabstop(cx));
 6757        assert(
 6758            editor,
 6759            cx,
 6760            indoc! {"
 6761                a.f(one, «two», three) b
 6762                a.f(one, «two», three) b
 6763                a.f(one, «two», three) b
 6764            "},
 6765        );
 6766
 6767        editor.move_to_prev_snippet_tabstop(cx);
 6768        assert(
 6769            editor,
 6770            cx,
 6771            indoc! {"
 6772                a.f(«one», two, «three») b
 6773                a.f(«one», two, «three») b
 6774                a.f(«one», two, «three») b
 6775            "},
 6776        );
 6777
 6778        assert!(editor.move_to_next_snippet_tabstop(cx));
 6779        assert(
 6780            editor,
 6781            cx,
 6782            indoc! {"
 6783                a.f(one, «two», three) b
 6784                a.f(one, «two», three) b
 6785                a.f(one, «two», three) b
 6786            "},
 6787        );
 6788        assert!(editor.move_to_next_snippet_tabstop(cx));
 6789        assert(
 6790            editor,
 6791            cx,
 6792            indoc! {"
 6793                a.f(one, two, three)ˇ b
 6794                a.f(one, two, three)ˇ b
 6795                a.f(one, two, three)ˇ b
 6796            "},
 6797        );
 6798
 6799        // As soon as the last tab stop is reached, snippet state is gone
 6800        editor.move_to_prev_snippet_tabstop(cx);
 6801        assert(
 6802            editor,
 6803            cx,
 6804            indoc! {"
 6805                a.f(one, two, three)ˇ b
 6806                a.f(one, two, three)ˇ b
 6807                a.f(one, two, three)ˇ b
 6808            "},
 6809        );
 6810    });
 6811}
 6812
 6813#[gpui::test]
 6814async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6815    init_test(cx, |_| {});
 6816
 6817    let fs = FakeFs::new(cx.executor());
 6818    fs.insert_file("/file.rs", Default::default()).await;
 6819
 6820    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6821
 6822    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6823    language_registry.add(rust_lang());
 6824    let mut fake_servers = language_registry.register_fake_lsp(
 6825        "Rust",
 6826        FakeLspAdapter {
 6827            capabilities: lsp::ServerCapabilities {
 6828                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6829                ..Default::default()
 6830            },
 6831            ..Default::default()
 6832        },
 6833    );
 6834
 6835    let buffer = project
 6836        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6837        .await
 6838        .unwrap();
 6839
 6840    cx.executor().start_waiting();
 6841    let fake_server = fake_servers.next().await.unwrap();
 6842
 6843    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6844    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6845    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6846    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6847
 6848    let save = editor
 6849        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6850        .unwrap();
 6851    fake_server
 6852        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6853            assert_eq!(
 6854                params.text_document.uri,
 6855                lsp::Url::from_file_path("/file.rs").unwrap()
 6856            );
 6857            assert_eq!(params.options.tab_size, 4);
 6858            Ok(Some(vec![lsp::TextEdit::new(
 6859                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6860                ", ".to_string(),
 6861            )]))
 6862        })
 6863        .next()
 6864        .await;
 6865    cx.executor().start_waiting();
 6866    save.await;
 6867
 6868    assert_eq!(
 6869        editor.update(cx, |editor, cx| editor.text(cx)),
 6870        "one, two\nthree\n"
 6871    );
 6872    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6873
 6874    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6875    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6876
 6877    // Ensure we can still save even if formatting hangs.
 6878    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6879        assert_eq!(
 6880            params.text_document.uri,
 6881            lsp::Url::from_file_path("/file.rs").unwrap()
 6882        );
 6883        futures::future::pending::<()>().await;
 6884        unreachable!()
 6885    });
 6886    let save = editor
 6887        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6888        .unwrap();
 6889    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6890    cx.executor().start_waiting();
 6891    save.await;
 6892    assert_eq!(
 6893        editor.update(cx, |editor, cx| editor.text(cx)),
 6894        "one\ntwo\nthree\n"
 6895    );
 6896    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6897
 6898    // For non-dirty buffer, no formatting request should be sent
 6899    let save = editor
 6900        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6901        .unwrap();
 6902    let _pending_format_request = fake_server
 6903        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6904            panic!("Should not be invoked on non-dirty buffer");
 6905        })
 6906        .next();
 6907    cx.executor().start_waiting();
 6908    save.await;
 6909
 6910    // Set rust language override and assert overridden tabsize is sent to language server
 6911    update_test_language_settings(cx, |settings| {
 6912        settings.languages.insert(
 6913            "Rust".into(),
 6914            LanguageSettingsContent {
 6915                tab_size: NonZeroU32::new(8),
 6916                ..Default::default()
 6917            },
 6918        );
 6919    });
 6920
 6921    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6922    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6923    let save = editor
 6924        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6925        .unwrap();
 6926    fake_server
 6927        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6928            assert_eq!(
 6929                params.text_document.uri,
 6930                lsp::Url::from_file_path("/file.rs").unwrap()
 6931            );
 6932            assert_eq!(params.options.tab_size, 8);
 6933            Ok(Some(vec![]))
 6934        })
 6935        .next()
 6936        .await;
 6937    cx.executor().start_waiting();
 6938    save.await;
 6939}
 6940
 6941#[gpui::test]
 6942async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6943    init_test(cx, |_| {});
 6944
 6945    let cols = 4;
 6946    let rows = 10;
 6947    let sample_text_1 = sample_text(rows, cols, 'a');
 6948    assert_eq!(
 6949        sample_text_1,
 6950        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6951    );
 6952    let sample_text_2 = sample_text(rows, cols, 'l');
 6953    assert_eq!(
 6954        sample_text_2,
 6955        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6956    );
 6957    let sample_text_3 = sample_text(rows, cols, 'v');
 6958    assert_eq!(
 6959        sample_text_3,
 6960        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6961    );
 6962
 6963    let fs = FakeFs::new(cx.executor());
 6964    fs.insert_tree(
 6965        "/a",
 6966        json!({
 6967            "main.rs": sample_text_1,
 6968            "other.rs": sample_text_2,
 6969            "lib.rs": sample_text_3,
 6970        }),
 6971    )
 6972    .await;
 6973
 6974    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6975    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6976    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6977
 6978    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6979    language_registry.add(rust_lang());
 6980    let mut fake_servers = language_registry.register_fake_lsp(
 6981        "Rust",
 6982        FakeLspAdapter {
 6983            capabilities: lsp::ServerCapabilities {
 6984                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6985                ..Default::default()
 6986            },
 6987            ..Default::default()
 6988        },
 6989    );
 6990
 6991    let worktree = project.update(cx, |project, cx| {
 6992        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6993        assert_eq!(worktrees.len(), 1);
 6994        worktrees.pop().unwrap()
 6995    });
 6996    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6997
 6998    let buffer_1 = project
 6999        .update(cx, |project, cx| {
 7000            project.open_buffer((worktree_id, "main.rs"), cx)
 7001        })
 7002        .await
 7003        .unwrap();
 7004    let buffer_2 = project
 7005        .update(cx, |project, cx| {
 7006            project.open_buffer((worktree_id, "other.rs"), cx)
 7007        })
 7008        .await
 7009        .unwrap();
 7010    let buffer_3 = project
 7011        .update(cx, |project, cx| {
 7012            project.open_buffer((worktree_id, "lib.rs"), cx)
 7013        })
 7014        .await
 7015        .unwrap();
 7016
 7017    let multi_buffer = cx.new_model(|cx| {
 7018        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7019        multi_buffer.push_excerpts(
 7020            buffer_1.clone(),
 7021            [
 7022                ExcerptRange {
 7023                    context: Point::new(0, 0)..Point::new(3, 0),
 7024                    primary: None,
 7025                },
 7026                ExcerptRange {
 7027                    context: Point::new(5, 0)..Point::new(7, 0),
 7028                    primary: None,
 7029                },
 7030                ExcerptRange {
 7031                    context: Point::new(9, 0)..Point::new(10, 4),
 7032                    primary: None,
 7033                },
 7034            ],
 7035            cx,
 7036        );
 7037        multi_buffer.push_excerpts(
 7038            buffer_2.clone(),
 7039            [
 7040                ExcerptRange {
 7041                    context: Point::new(0, 0)..Point::new(3, 0),
 7042                    primary: None,
 7043                },
 7044                ExcerptRange {
 7045                    context: Point::new(5, 0)..Point::new(7, 0),
 7046                    primary: None,
 7047                },
 7048                ExcerptRange {
 7049                    context: Point::new(9, 0)..Point::new(10, 4),
 7050                    primary: None,
 7051                },
 7052            ],
 7053            cx,
 7054        );
 7055        multi_buffer.push_excerpts(
 7056            buffer_3.clone(),
 7057            [
 7058                ExcerptRange {
 7059                    context: Point::new(0, 0)..Point::new(3, 0),
 7060                    primary: None,
 7061                },
 7062                ExcerptRange {
 7063                    context: Point::new(5, 0)..Point::new(7, 0),
 7064                    primary: None,
 7065                },
 7066                ExcerptRange {
 7067                    context: Point::new(9, 0)..Point::new(10, 4),
 7068                    primary: None,
 7069                },
 7070            ],
 7071            cx,
 7072        );
 7073        multi_buffer
 7074    });
 7075    let multi_buffer_editor = cx.new_view(|cx| {
 7076        Editor::new(
 7077            EditorMode::Full,
 7078            multi_buffer,
 7079            Some(project.clone()),
 7080            true,
 7081            cx,
 7082        )
 7083    });
 7084
 7085    multi_buffer_editor.update(cx, |editor, cx| {
 7086        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7087        editor.insert("|one|two|three|", cx);
 7088    });
 7089    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7090    multi_buffer_editor.update(cx, |editor, cx| {
 7091        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7092            s.select_ranges(Some(60..70))
 7093        });
 7094        editor.insert("|four|five|six|", cx);
 7095    });
 7096    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7097
 7098    // First two buffers should be edited, but not the third one.
 7099    assert_eq!(
 7100        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7101        "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}",
 7102    );
 7103    buffer_1.update(cx, |buffer, _| {
 7104        assert!(buffer.is_dirty());
 7105        assert_eq!(
 7106            buffer.text(),
 7107            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7108        )
 7109    });
 7110    buffer_2.update(cx, |buffer, _| {
 7111        assert!(buffer.is_dirty());
 7112        assert_eq!(
 7113            buffer.text(),
 7114            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7115        )
 7116    });
 7117    buffer_3.update(cx, |buffer, _| {
 7118        assert!(!buffer.is_dirty());
 7119        assert_eq!(buffer.text(), sample_text_3,)
 7120    });
 7121
 7122    cx.executor().start_waiting();
 7123    let save = multi_buffer_editor
 7124        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7125        .unwrap();
 7126
 7127    let fake_server = fake_servers.next().await.unwrap();
 7128    fake_server
 7129        .server
 7130        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7131            Ok(Some(vec![lsp::TextEdit::new(
 7132                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7133                format!("[{} formatted]", params.text_document.uri),
 7134            )]))
 7135        })
 7136        .detach();
 7137    save.await;
 7138
 7139    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7140    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7141    assert_eq!(
 7142        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7143        "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}",
 7144    );
 7145    buffer_1.update(cx, |buffer, _| {
 7146        assert!(!buffer.is_dirty());
 7147        assert_eq!(
 7148            buffer.text(),
 7149            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7150        )
 7151    });
 7152    buffer_2.update(cx, |buffer, _| {
 7153        assert!(!buffer.is_dirty());
 7154        assert_eq!(
 7155            buffer.text(),
 7156            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7157        )
 7158    });
 7159    buffer_3.update(cx, |buffer, _| {
 7160        assert!(!buffer.is_dirty());
 7161        assert_eq!(buffer.text(), sample_text_3,)
 7162    });
 7163}
 7164
 7165#[gpui::test]
 7166async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7167    init_test(cx, |_| {});
 7168
 7169    let fs = FakeFs::new(cx.executor());
 7170    fs.insert_file("/file.rs", Default::default()).await;
 7171
 7172    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7173
 7174    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7175    language_registry.add(rust_lang());
 7176    let mut fake_servers = language_registry.register_fake_lsp(
 7177        "Rust",
 7178        FakeLspAdapter {
 7179            capabilities: lsp::ServerCapabilities {
 7180                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7181                ..Default::default()
 7182            },
 7183            ..Default::default()
 7184        },
 7185    );
 7186
 7187    let buffer = project
 7188        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7189        .await
 7190        .unwrap();
 7191
 7192    cx.executor().start_waiting();
 7193    let fake_server = fake_servers.next().await.unwrap();
 7194
 7195    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7196    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7197    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7198    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7199
 7200    let save = editor
 7201        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7202        .unwrap();
 7203    fake_server
 7204        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7205            assert_eq!(
 7206                params.text_document.uri,
 7207                lsp::Url::from_file_path("/file.rs").unwrap()
 7208            );
 7209            assert_eq!(params.options.tab_size, 4);
 7210            Ok(Some(vec![lsp::TextEdit::new(
 7211                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7212                ", ".to_string(),
 7213            )]))
 7214        })
 7215        .next()
 7216        .await;
 7217    cx.executor().start_waiting();
 7218    save.await;
 7219    assert_eq!(
 7220        editor.update(cx, |editor, cx| editor.text(cx)),
 7221        "one, two\nthree\n"
 7222    );
 7223    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7224
 7225    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7226    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7227
 7228    // Ensure we can still save even if formatting hangs.
 7229    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7230        move |params, _| async move {
 7231            assert_eq!(
 7232                params.text_document.uri,
 7233                lsp::Url::from_file_path("/file.rs").unwrap()
 7234            );
 7235            futures::future::pending::<()>().await;
 7236            unreachable!()
 7237        },
 7238    );
 7239    let save = editor
 7240        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7241        .unwrap();
 7242    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7243    cx.executor().start_waiting();
 7244    save.await;
 7245    assert_eq!(
 7246        editor.update(cx, |editor, cx| editor.text(cx)),
 7247        "one\ntwo\nthree\n"
 7248    );
 7249    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7250
 7251    // For non-dirty buffer, no formatting request should be sent
 7252    let save = editor
 7253        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7254        .unwrap();
 7255    let _pending_format_request = fake_server
 7256        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7257            panic!("Should not be invoked on non-dirty buffer");
 7258        })
 7259        .next();
 7260    cx.executor().start_waiting();
 7261    save.await;
 7262
 7263    // Set Rust language override and assert overridden tabsize is sent to language server
 7264    update_test_language_settings(cx, |settings| {
 7265        settings.languages.insert(
 7266            "Rust".into(),
 7267            LanguageSettingsContent {
 7268                tab_size: NonZeroU32::new(8),
 7269                ..Default::default()
 7270            },
 7271        );
 7272    });
 7273
 7274    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7275    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7276    let save = editor
 7277        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7278        .unwrap();
 7279    fake_server
 7280        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7281            assert_eq!(
 7282                params.text_document.uri,
 7283                lsp::Url::from_file_path("/file.rs").unwrap()
 7284            );
 7285            assert_eq!(params.options.tab_size, 8);
 7286            Ok(Some(vec![]))
 7287        })
 7288        .next()
 7289        .await;
 7290    cx.executor().start_waiting();
 7291    save.await;
 7292}
 7293
 7294#[gpui::test]
 7295async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7296    init_test(cx, |settings| {
 7297        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7298            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7299        ))
 7300    });
 7301
 7302    let fs = FakeFs::new(cx.executor());
 7303    fs.insert_file("/file.rs", Default::default()).await;
 7304
 7305    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7306
 7307    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7308    language_registry.add(Arc::new(Language::new(
 7309        LanguageConfig {
 7310            name: "Rust".into(),
 7311            matcher: LanguageMatcher {
 7312                path_suffixes: vec!["rs".to_string()],
 7313                ..Default::default()
 7314            },
 7315            ..LanguageConfig::default()
 7316        },
 7317        Some(tree_sitter_rust::LANGUAGE.into()),
 7318    )));
 7319    update_test_language_settings(cx, |settings| {
 7320        // Enable Prettier formatting for the same buffer, and ensure
 7321        // LSP is called instead of Prettier.
 7322        settings.defaults.prettier = Some(PrettierSettings {
 7323            allowed: true,
 7324            ..PrettierSettings::default()
 7325        });
 7326    });
 7327    let mut fake_servers = language_registry.register_fake_lsp(
 7328        "Rust",
 7329        FakeLspAdapter {
 7330            capabilities: lsp::ServerCapabilities {
 7331                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7332                ..Default::default()
 7333            },
 7334            ..Default::default()
 7335        },
 7336    );
 7337
 7338    let buffer = project
 7339        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7340        .await
 7341        .unwrap();
 7342
 7343    cx.executor().start_waiting();
 7344    let fake_server = fake_servers.next().await.unwrap();
 7345
 7346    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7347    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7348    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7349
 7350    let format = editor
 7351        .update(cx, |editor, cx| {
 7352            editor.perform_format(
 7353                project.clone(),
 7354                FormatTrigger::Manual,
 7355                FormatTarget::Buffer,
 7356                cx,
 7357            )
 7358        })
 7359        .unwrap();
 7360    fake_server
 7361        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7362            assert_eq!(
 7363                params.text_document.uri,
 7364                lsp::Url::from_file_path("/file.rs").unwrap()
 7365            );
 7366            assert_eq!(params.options.tab_size, 4);
 7367            Ok(Some(vec![lsp::TextEdit::new(
 7368                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7369                ", ".to_string(),
 7370            )]))
 7371        })
 7372        .next()
 7373        .await;
 7374    cx.executor().start_waiting();
 7375    format.await;
 7376    assert_eq!(
 7377        editor.update(cx, |editor, cx| editor.text(cx)),
 7378        "one, two\nthree\n"
 7379    );
 7380
 7381    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7382    // Ensure we don't lock if formatting hangs.
 7383    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7384        assert_eq!(
 7385            params.text_document.uri,
 7386            lsp::Url::from_file_path("/file.rs").unwrap()
 7387        );
 7388        futures::future::pending::<()>().await;
 7389        unreachable!()
 7390    });
 7391    let format = editor
 7392        .update(cx, |editor, cx| {
 7393            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7394        })
 7395        .unwrap();
 7396    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7397    cx.executor().start_waiting();
 7398    format.await;
 7399    assert_eq!(
 7400        editor.update(cx, |editor, cx| editor.text(cx)),
 7401        "one\ntwo\nthree\n"
 7402    );
 7403}
 7404
 7405#[gpui::test]
 7406async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7407    init_test(cx, |_| {});
 7408
 7409    let mut cx = EditorLspTestContext::new_rust(
 7410        lsp::ServerCapabilities {
 7411            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7412            ..Default::default()
 7413        },
 7414        cx,
 7415    )
 7416    .await;
 7417
 7418    cx.set_state(indoc! {"
 7419        one.twoˇ
 7420    "});
 7421
 7422    // The format request takes a long time. When it completes, it inserts
 7423    // a newline and an indent before the `.`
 7424    cx.lsp
 7425        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7426            let executor = cx.background_executor().clone();
 7427            async move {
 7428                executor.timer(Duration::from_millis(100)).await;
 7429                Ok(Some(vec![lsp::TextEdit {
 7430                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7431                    new_text: "\n    ".into(),
 7432                }]))
 7433            }
 7434        });
 7435
 7436    // Submit a format request.
 7437    let format_1 = cx
 7438        .update_editor(|editor, cx| editor.format(&Format, cx))
 7439        .unwrap();
 7440    cx.executor().run_until_parked();
 7441
 7442    // Submit a second format request.
 7443    let format_2 = cx
 7444        .update_editor(|editor, cx| editor.format(&Format, cx))
 7445        .unwrap();
 7446    cx.executor().run_until_parked();
 7447
 7448    // Wait for both format requests to complete
 7449    cx.executor().advance_clock(Duration::from_millis(200));
 7450    cx.executor().start_waiting();
 7451    format_1.await.unwrap();
 7452    cx.executor().start_waiting();
 7453    format_2.await.unwrap();
 7454
 7455    // The formatting edits only happens once.
 7456    cx.assert_editor_state(indoc! {"
 7457        one
 7458            .twoˇ
 7459    "});
 7460}
 7461
 7462#[gpui::test]
 7463async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7464    init_test(cx, |settings| {
 7465        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7466    });
 7467
 7468    let mut cx = EditorLspTestContext::new_rust(
 7469        lsp::ServerCapabilities {
 7470            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7471            ..Default::default()
 7472        },
 7473        cx,
 7474    )
 7475    .await;
 7476
 7477    // Set up a buffer white some trailing whitespace and no trailing newline.
 7478    cx.set_state(
 7479        &[
 7480            "one ",   //
 7481            "twoˇ",   //
 7482            "three ", //
 7483            "four",   //
 7484        ]
 7485        .join("\n"),
 7486    );
 7487
 7488    // Submit a format request.
 7489    let format = cx
 7490        .update_editor(|editor, cx| editor.format(&Format, cx))
 7491        .unwrap();
 7492
 7493    // Record which buffer changes have been sent to the language server
 7494    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7495    cx.lsp
 7496        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7497            let buffer_changes = buffer_changes.clone();
 7498            move |params, _| {
 7499                buffer_changes.lock().extend(
 7500                    params
 7501                        .content_changes
 7502                        .into_iter()
 7503                        .map(|e| (e.range.unwrap(), e.text)),
 7504                );
 7505            }
 7506        });
 7507
 7508    // Handle formatting requests to the language server.
 7509    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7510        let buffer_changes = buffer_changes.clone();
 7511        move |_, _| {
 7512            // When formatting is requested, trailing whitespace has already been stripped,
 7513            // and the trailing newline has already been added.
 7514            assert_eq!(
 7515                &buffer_changes.lock()[1..],
 7516                &[
 7517                    (
 7518                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7519                        "".into()
 7520                    ),
 7521                    (
 7522                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7523                        "".into()
 7524                    ),
 7525                    (
 7526                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7527                        "\n".into()
 7528                    ),
 7529                ]
 7530            );
 7531
 7532            // Insert blank lines between each line of the buffer.
 7533            async move {
 7534                Ok(Some(vec![
 7535                    lsp::TextEdit {
 7536                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7537                        new_text: "\n".into(),
 7538                    },
 7539                    lsp::TextEdit {
 7540                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7541                        new_text: "\n".into(),
 7542                    },
 7543                ]))
 7544            }
 7545        }
 7546    });
 7547
 7548    // After formatting the buffer, the trailing whitespace is stripped,
 7549    // a newline is appended, and the edits provided by the language server
 7550    // have been applied.
 7551    format.await.unwrap();
 7552    cx.assert_editor_state(
 7553        &[
 7554            "one",   //
 7555            "",      //
 7556            "twoˇ",  //
 7557            "",      //
 7558            "three", //
 7559            "four",  //
 7560            "",      //
 7561        ]
 7562        .join("\n"),
 7563    );
 7564
 7565    // Undoing the formatting undoes the trailing whitespace removal, the
 7566    // trailing newline, and the LSP edits.
 7567    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7568    cx.assert_editor_state(
 7569        &[
 7570            "one ",   //
 7571            "twoˇ",   //
 7572            "three ", //
 7573            "four",   //
 7574        ]
 7575        .join("\n"),
 7576    );
 7577}
 7578
 7579#[gpui::test]
 7580async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7581    cx: &mut gpui::TestAppContext,
 7582) {
 7583    init_test(cx, |_| {});
 7584
 7585    cx.update(|cx| {
 7586        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7587            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7588                settings.auto_signature_help = Some(true);
 7589            });
 7590        });
 7591    });
 7592
 7593    let mut cx = EditorLspTestContext::new_rust(
 7594        lsp::ServerCapabilities {
 7595            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7596                ..Default::default()
 7597            }),
 7598            ..Default::default()
 7599        },
 7600        cx,
 7601    )
 7602    .await;
 7603
 7604    let language = Language::new(
 7605        LanguageConfig {
 7606            name: "Rust".into(),
 7607            brackets: BracketPairConfig {
 7608                pairs: vec![
 7609                    BracketPair {
 7610                        start: "{".to_string(),
 7611                        end: "}".to_string(),
 7612                        close: true,
 7613                        surround: true,
 7614                        newline: true,
 7615                    },
 7616                    BracketPair {
 7617                        start: "(".to_string(),
 7618                        end: ")".to_string(),
 7619                        close: true,
 7620                        surround: true,
 7621                        newline: true,
 7622                    },
 7623                    BracketPair {
 7624                        start: "/*".to_string(),
 7625                        end: " */".to_string(),
 7626                        close: true,
 7627                        surround: true,
 7628                        newline: true,
 7629                    },
 7630                    BracketPair {
 7631                        start: "[".to_string(),
 7632                        end: "]".to_string(),
 7633                        close: false,
 7634                        surround: false,
 7635                        newline: true,
 7636                    },
 7637                    BracketPair {
 7638                        start: "\"".to_string(),
 7639                        end: "\"".to_string(),
 7640                        close: true,
 7641                        surround: true,
 7642                        newline: false,
 7643                    },
 7644                    BracketPair {
 7645                        start: "<".to_string(),
 7646                        end: ">".to_string(),
 7647                        close: false,
 7648                        surround: true,
 7649                        newline: true,
 7650                    },
 7651                ],
 7652                ..Default::default()
 7653            },
 7654            autoclose_before: "})]".to_string(),
 7655            ..Default::default()
 7656        },
 7657        Some(tree_sitter_rust::LANGUAGE.into()),
 7658    );
 7659    let language = Arc::new(language);
 7660
 7661    cx.language_registry().add(language.clone());
 7662    cx.update_buffer(|buffer, cx| {
 7663        buffer.set_language(Some(language), cx);
 7664    });
 7665
 7666    cx.set_state(
 7667        &r#"
 7668            fn main() {
 7669                sampleˇ
 7670            }
 7671        "#
 7672        .unindent(),
 7673    );
 7674
 7675    cx.update_editor(|view, cx| {
 7676        view.handle_input("(", cx);
 7677    });
 7678    cx.assert_editor_state(
 7679        &"
 7680            fn main() {
 7681                sample(ˇ)
 7682            }
 7683        "
 7684        .unindent(),
 7685    );
 7686
 7687    let mocked_response = lsp::SignatureHelp {
 7688        signatures: vec![lsp::SignatureInformation {
 7689            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7690            documentation: None,
 7691            parameters: Some(vec![
 7692                lsp::ParameterInformation {
 7693                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7694                    documentation: None,
 7695                },
 7696                lsp::ParameterInformation {
 7697                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7698                    documentation: None,
 7699                },
 7700            ]),
 7701            active_parameter: None,
 7702        }],
 7703        active_signature: Some(0),
 7704        active_parameter: Some(0),
 7705    };
 7706    handle_signature_help_request(&mut cx, mocked_response).await;
 7707
 7708    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7709        .await;
 7710
 7711    cx.editor(|editor, _| {
 7712        let signature_help_state = editor.signature_help_state.popover().cloned();
 7713        assert!(signature_help_state.is_some());
 7714        let ParsedMarkdown {
 7715            text, highlights, ..
 7716        } = signature_help_state.unwrap().parsed_content;
 7717        assert_eq!(text, "param1: u8, param2: u8");
 7718        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7719    });
 7720}
 7721
 7722#[gpui::test]
 7723async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7724    init_test(cx, |_| {});
 7725
 7726    cx.update(|cx| {
 7727        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7728            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7729                settings.auto_signature_help = Some(false);
 7730                settings.show_signature_help_after_edits = Some(false);
 7731            });
 7732        });
 7733    });
 7734
 7735    let mut cx = EditorLspTestContext::new_rust(
 7736        lsp::ServerCapabilities {
 7737            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7738                ..Default::default()
 7739            }),
 7740            ..Default::default()
 7741        },
 7742        cx,
 7743    )
 7744    .await;
 7745
 7746    let language = Language::new(
 7747        LanguageConfig {
 7748            name: "Rust".into(),
 7749            brackets: BracketPairConfig {
 7750                pairs: vec![
 7751                    BracketPair {
 7752                        start: "{".to_string(),
 7753                        end: "}".to_string(),
 7754                        close: true,
 7755                        surround: true,
 7756                        newline: true,
 7757                    },
 7758                    BracketPair {
 7759                        start: "(".to_string(),
 7760                        end: ")".to_string(),
 7761                        close: true,
 7762                        surround: true,
 7763                        newline: true,
 7764                    },
 7765                    BracketPair {
 7766                        start: "/*".to_string(),
 7767                        end: " */".to_string(),
 7768                        close: true,
 7769                        surround: true,
 7770                        newline: true,
 7771                    },
 7772                    BracketPair {
 7773                        start: "[".to_string(),
 7774                        end: "]".to_string(),
 7775                        close: false,
 7776                        surround: false,
 7777                        newline: true,
 7778                    },
 7779                    BracketPair {
 7780                        start: "\"".to_string(),
 7781                        end: "\"".to_string(),
 7782                        close: true,
 7783                        surround: true,
 7784                        newline: false,
 7785                    },
 7786                    BracketPair {
 7787                        start: "<".to_string(),
 7788                        end: ">".to_string(),
 7789                        close: false,
 7790                        surround: true,
 7791                        newline: true,
 7792                    },
 7793                ],
 7794                ..Default::default()
 7795            },
 7796            autoclose_before: "})]".to_string(),
 7797            ..Default::default()
 7798        },
 7799        Some(tree_sitter_rust::LANGUAGE.into()),
 7800    );
 7801    let language = Arc::new(language);
 7802
 7803    cx.language_registry().add(language.clone());
 7804    cx.update_buffer(|buffer, cx| {
 7805        buffer.set_language(Some(language), cx);
 7806    });
 7807
 7808    // Ensure that signature_help is not called when no signature help is enabled.
 7809    cx.set_state(
 7810        &r#"
 7811            fn main() {
 7812                sampleˇ
 7813            }
 7814        "#
 7815        .unindent(),
 7816    );
 7817    cx.update_editor(|view, cx| {
 7818        view.handle_input("(", cx);
 7819    });
 7820    cx.assert_editor_state(
 7821        &"
 7822            fn main() {
 7823                sample(ˇ)
 7824            }
 7825        "
 7826        .unindent(),
 7827    );
 7828    cx.editor(|editor, _| {
 7829        assert!(editor.signature_help_state.task().is_none());
 7830    });
 7831
 7832    let mocked_response = lsp::SignatureHelp {
 7833        signatures: vec![lsp::SignatureInformation {
 7834            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7835            documentation: None,
 7836            parameters: Some(vec![
 7837                lsp::ParameterInformation {
 7838                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7839                    documentation: None,
 7840                },
 7841                lsp::ParameterInformation {
 7842                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7843                    documentation: None,
 7844                },
 7845            ]),
 7846            active_parameter: None,
 7847        }],
 7848        active_signature: Some(0),
 7849        active_parameter: Some(0),
 7850    };
 7851
 7852    // Ensure that signature_help is called when enabled afte edits
 7853    cx.update(|cx| {
 7854        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7855            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7856                settings.auto_signature_help = Some(false);
 7857                settings.show_signature_help_after_edits = Some(true);
 7858            });
 7859        });
 7860    });
 7861    cx.set_state(
 7862        &r#"
 7863            fn main() {
 7864                sampleˇ
 7865            }
 7866        "#
 7867        .unindent(),
 7868    );
 7869    cx.update_editor(|view, cx| {
 7870        view.handle_input("(", cx);
 7871    });
 7872    cx.assert_editor_state(
 7873        &"
 7874            fn main() {
 7875                sample(ˇ)
 7876            }
 7877        "
 7878        .unindent(),
 7879    );
 7880    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7881    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7882        .await;
 7883    cx.update_editor(|editor, _| {
 7884        let signature_help_state = editor.signature_help_state.popover().cloned();
 7885        assert!(signature_help_state.is_some());
 7886        let ParsedMarkdown {
 7887            text, highlights, ..
 7888        } = signature_help_state.unwrap().parsed_content;
 7889        assert_eq!(text, "param1: u8, param2: u8");
 7890        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7891        editor.signature_help_state = SignatureHelpState::default();
 7892    });
 7893
 7894    // Ensure that signature_help is called when auto signature help override is enabled
 7895    cx.update(|cx| {
 7896        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7897            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7898                settings.auto_signature_help = Some(true);
 7899                settings.show_signature_help_after_edits = Some(false);
 7900            });
 7901        });
 7902    });
 7903    cx.set_state(
 7904        &r#"
 7905            fn main() {
 7906                sampleˇ
 7907            }
 7908        "#
 7909        .unindent(),
 7910    );
 7911    cx.update_editor(|view, cx| {
 7912        view.handle_input("(", cx);
 7913    });
 7914    cx.assert_editor_state(
 7915        &"
 7916            fn main() {
 7917                sample(ˇ)
 7918            }
 7919        "
 7920        .unindent(),
 7921    );
 7922    handle_signature_help_request(&mut cx, mocked_response).await;
 7923    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7924        .await;
 7925    cx.editor(|editor, _| {
 7926        let signature_help_state = editor.signature_help_state.popover().cloned();
 7927        assert!(signature_help_state.is_some());
 7928        let ParsedMarkdown {
 7929            text, highlights, ..
 7930        } = signature_help_state.unwrap().parsed_content;
 7931        assert_eq!(text, "param1: u8, param2: u8");
 7932        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7933    });
 7934}
 7935
 7936#[gpui::test]
 7937async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7938    init_test(cx, |_| {});
 7939    cx.update(|cx| {
 7940        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7941            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7942                settings.auto_signature_help = Some(true);
 7943            });
 7944        });
 7945    });
 7946
 7947    let mut cx = EditorLspTestContext::new_rust(
 7948        lsp::ServerCapabilities {
 7949            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7950                ..Default::default()
 7951            }),
 7952            ..Default::default()
 7953        },
 7954        cx,
 7955    )
 7956    .await;
 7957
 7958    // A test that directly calls `show_signature_help`
 7959    cx.update_editor(|editor, cx| {
 7960        editor.show_signature_help(&ShowSignatureHelp, cx);
 7961    });
 7962
 7963    let mocked_response = lsp::SignatureHelp {
 7964        signatures: vec![lsp::SignatureInformation {
 7965            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7966            documentation: None,
 7967            parameters: Some(vec![
 7968                lsp::ParameterInformation {
 7969                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7970                    documentation: None,
 7971                },
 7972                lsp::ParameterInformation {
 7973                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7974                    documentation: None,
 7975                },
 7976            ]),
 7977            active_parameter: None,
 7978        }],
 7979        active_signature: Some(0),
 7980        active_parameter: Some(0),
 7981    };
 7982    handle_signature_help_request(&mut cx, mocked_response).await;
 7983
 7984    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7985        .await;
 7986
 7987    cx.editor(|editor, _| {
 7988        let signature_help_state = editor.signature_help_state.popover().cloned();
 7989        assert!(signature_help_state.is_some());
 7990        let ParsedMarkdown {
 7991            text, highlights, ..
 7992        } = signature_help_state.unwrap().parsed_content;
 7993        assert_eq!(text, "param1: u8, param2: u8");
 7994        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7995    });
 7996
 7997    // When exiting outside from inside the brackets, `signature_help` is closed.
 7998    cx.set_state(indoc! {"
 7999        fn main() {
 8000            sample(ˇ);
 8001        }
 8002
 8003        fn sample(param1: u8, param2: u8) {}
 8004    "});
 8005
 8006    cx.update_editor(|editor, cx| {
 8007        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8008    });
 8009
 8010    let mocked_response = lsp::SignatureHelp {
 8011        signatures: Vec::new(),
 8012        active_signature: None,
 8013        active_parameter: None,
 8014    };
 8015    handle_signature_help_request(&mut cx, mocked_response).await;
 8016
 8017    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8018        .await;
 8019
 8020    cx.editor(|editor, _| {
 8021        assert!(!editor.signature_help_state.is_shown());
 8022    });
 8023
 8024    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8025    cx.set_state(indoc! {"
 8026        fn main() {
 8027            sample(ˇ);
 8028        }
 8029
 8030        fn sample(param1: u8, param2: u8) {}
 8031    "});
 8032
 8033    let mocked_response = lsp::SignatureHelp {
 8034        signatures: vec![lsp::SignatureInformation {
 8035            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8036            documentation: None,
 8037            parameters: Some(vec![
 8038                lsp::ParameterInformation {
 8039                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8040                    documentation: None,
 8041                },
 8042                lsp::ParameterInformation {
 8043                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8044                    documentation: None,
 8045                },
 8046            ]),
 8047            active_parameter: None,
 8048        }],
 8049        active_signature: Some(0),
 8050        active_parameter: Some(0),
 8051    };
 8052    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8053    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8054        .await;
 8055    cx.editor(|editor, _| {
 8056        assert!(editor.signature_help_state.is_shown());
 8057    });
 8058
 8059    // Restore the popover with more parameter input
 8060    cx.set_state(indoc! {"
 8061        fn main() {
 8062            sample(param1, param2ˇ);
 8063        }
 8064
 8065        fn sample(param1: u8, param2: u8) {}
 8066    "});
 8067
 8068    let mocked_response = lsp::SignatureHelp {
 8069        signatures: vec![lsp::SignatureInformation {
 8070            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8071            documentation: None,
 8072            parameters: Some(vec![
 8073                lsp::ParameterInformation {
 8074                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8075                    documentation: None,
 8076                },
 8077                lsp::ParameterInformation {
 8078                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8079                    documentation: None,
 8080                },
 8081            ]),
 8082            active_parameter: None,
 8083        }],
 8084        active_signature: Some(0),
 8085        active_parameter: Some(1),
 8086    };
 8087    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8088    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8089        .await;
 8090
 8091    // When selecting a range, the popover is gone.
 8092    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8093    cx.update_editor(|editor, cx| {
 8094        editor.change_selections(None, cx, |s| {
 8095            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8096        })
 8097    });
 8098    cx.assert_editor_state(indoc! {"
 8099        fn main() {
 8100            sample(param1, «ˇparam2»);
 8101        }
 8102
 8103        fn sample(param1: u8, param2: u8) {}
 8104    "});
 8105    cx.editor(|editor, _| {
 8106        assert!(!editor.signature_help_state.is_shown());
 8107    });
 8108
 8109    // When unselecting again, the popover is back if within the brackets.
 8110    cx.update_editor(|editor, cx| {
 8111        editor.change_selections(None, cx, |s| {
 8112            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8113        })
 8114    });
 8115    cx.assert_editor_state(indoc! {"
 8116        fn main() {
 8117            sample(param1, ˇparam2);
 8118        }
 8119
 8120        fn sample(param1: u8, param2: u8) {}
 8121    "});
 8122    handle_signature_help_request(&mut cx, mocked_response).await;
 8123    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8124        .await;
 8125    cx.editor(|editor, _| {
 8126        assert!(editor.signature_help_state.is_shown());
 8127    });
 8128
 8129    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8130    cx.update_editor(|editor, cx| {
 8131        editor.change_selections(None, cx, |s| {
 8132            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8133            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8134        })
 8135    });
 8136    cx.assert_editor_state(indoc! {"
 8137        fn main() {
 8138            sample(param1, ˇparam2);
 8139        }
 8140
 8141        fn sample(param1: u8, param2: u8) {}
 8142    "});
 8143
 8144    let mocked_response = lsp::SignatureHelp {
 8145        signatures: vec![lsp::SignatureInformation {
 8146            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8147            documentation: None,
 8148            parameters: Some(vec![
 8149                lsp::ParameterInformation {
 8150                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8151                    documentation: None,
 8152                },
 8153                lsp::ParameterInformation {
 8154                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8155                    documentation: None,
 8156                },
 8157            ]),
 8158            active_parameter: None,
 8159        }],
 8160        active_signature: Some(0),
 8161        active_parameter: Some(1),
 8162    };
 8163    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8164    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8165        .await;
 8166    cx.update_editor(|editor, cx| {
 8167        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8168    });
 8169    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8170        .await;
 8171    cx.update_editor(|editor, cx| {
 8172        editor.change_selections(None, cx, |s| {
 8173            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8174        })
 8175    });
 8176    cx.assert_editor_state(indoc! {"
 8177        fn main() {
 8178            sample(param1, «ˇparam2»);
 8179        }
 8180
 8181        fn sample(param1: u8, param2: u8) {}
 8182    "});
 8183    cx.update_editor(|editor, cx| {
 8184        editor.change_selections(None, cx, |s| {
 8185            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8186        })
 8187    });
 8188    cx.assert_editor_state(indoc! {"
 8189        fn main() {
 8190            sample(param1, ˇparam2);
 8191        }
 8192
 8193        fn sample(param1: u8, param2: u8) {}
 8194    "});
 8195    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8196        .await;
 8197}
 8198
 8199#[gpui::test]
 8200async fn test_completion(cx: &mut gpui::TestAppContext) {
 8201    init_test(cx, |_| {});
 8202
 8203    let mut cx = EditorLspTestContext::new_rust(
 8204        lsp::ServerCapabilities {
 8205            completion_provider: Some(lsp::CompletionOptions {
 8206                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8207                resolve_provider: Some(true),
 8208                ..Default::default()
 8209            }),
 8210            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8211            ..Default::default()
 8212        },
 8213        cx,
 8214    )
 8215    .await;
 8216    let counter = Arc::new(AtomicUsize::new(0));
 8217
 8218    cx.set_state(indoc! {"
 8219        oneˇ
 8220        two
 8221        three
 8222    "});
 8223    cx.simulate_keystroke(".");
 8224    handle_completion_request(
 8225        &mut cx,
 8226        indoc! {"
 8227            one.|<>
 8228            two
 8229            three
 8230        "},
 8231        vec!["first_completion", "second_completion"],
 8232        counter.clone(),
 8233    )
 8234    .await;
 8235    cx.condition(|editor, _| editor.context_menu_visible())
 8236        .await;
 8237    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8238
 8239    let _handler = handle_signature_help_request(
 8240        &mut cx,
 8241        lsp::SignatureHelp {
 8242            signatures: vec![lsp::SignatureInformation {
 8243                label: "test signature".to_string(),
 8244                documentation: None,
 8245                parameters: Some(vec![lsp::ParameterInformation {
 8246                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8247                    documentation: None,
 8248                }]),
 8249                active_parameter: None,
 8250            }],
 8251            active_signature: None,
 8252            active_parameter: None,
 8253        },
 8254    );
 8255    cx.update_editor(|editor, cx| {
 8256        assert!(
 8257            !editor.signature_help_state.is_shown(),
 8258            "No signature help was called for"
 8259        );
 8260        editor.show_signature_help(&ShowSignatureHelp, cx);
 8261    });
 8262    cx.run_until_parked();
 8263    cx.update_editor(|editor, _| {
 8264        assert!(
 8265            !editor.signature_help_state.is_shown(),
 8266            "No signature help should be shown when completions menu is open"
 8267        );
 8268    });
 8269
 8270    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8271        editor.context_menu_next(&Default::default(), cx);
 8272        editor
 8273            .confirm_completion(&ConfirmCompletion::default(), cx)
 8274            .unwrap()
 8275    });
 8276    cx.assert_editor_state(indoc! {"
 8277        one.second_completionˇ
 8278        two
 8279        three
 8280    "});
 8281
 8282    handle_resolve_completion_request(
 8283        &mut cx,
 8284        Some(vec![
 8285            (
 8286                //This overlaps with the primary completion edit which is
 8287                //misbehavior from the LSP spec, test that we filter it out
 8288                indoc! {"
 8289                    one.second_ˇcompletion
 8290                    two
 8291                    threeˇ
 8292                "},
 8293                "overlapping additional edit",
 8294            ),
 8295            (
 8296                indoc! {"
 8297                    one.second_completion
 8298                    two
 8299                    threeˇ
 8300                "},
 8301                "\nadditional edit",
 8302            ),
 8303        ]),
 8304    )
 8305    .await;
 8306    apply_additional_edits.await.unwrap();
 8307    cx.assert_editor_state(indoc! {"
 8308        one.second_completionˇ
 8309        two
 8310        three
 8311        additional edit
 8312    "});
 8313
 8314    cx.set_state(indoc! {"
 8315        one.second_completion
 8316        twoˇ
 8317        threeˇ
 8318        additional edit
 8319    "});
 8320    cx.simulate_keystroke(" ");
 8321    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8322    cx.simulate_keystroke("s");
 8323    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8324
 8325    cx.assert_editor_state(indoc! {"
 8326        one.second_completion
 8327        two sˇ
 8328        three sˇ
 8329        additional edit
 8330    "});
 8331    handle_completion_request(
 8332        &mut cx,
 8333        indoc! {"
 8334            one.second_completion
 8335            two s
 8336            three <s|>
 8337            additional edit
 8338        "},
 8339        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8340        counter.clone(),
 8341    )
 8342    .await;
 8343    cx.condition(|editor, _| editor.context_menu_visible())
 8344        .await;
 8345    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8346
 8347    cx.simulate_keystroke("i");
 8348
 8349    handle_completion_request(
 8350        &mut cx,
 8351        indoc! {"
 8352            one.second_completion
 8353            two si
 8354            three <si|>
 8355            additional edit
 8356        "},
 8357        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8358        counter.clone(),
 8359    )
 8360    .await;
 8361    cx.condition(|editor, _| editor.context_menu_visible())
 8362        .await;
 8363    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8364
 8365    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8366        editor
 8367            .confirm_completion(&ConfirmCompletion::default(), cx)
 8368            .unwrap()
 8369    });
 8370    cx.assert_editor_state(indoc! {"
 8371        one.second_completion
 8372        two sixth_completionˇ
 8373        three sixth_completionˇ
 8374        additional edit
 8375    "});
 8376
 8377    handle_resolve_completion_request(&mut cx, None).await;
 8378    apply_additional_edits.await.unwrap();
 8379
 8380    cx.update(|cx| {
 8381        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8382            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8383                settings.show_completions_on_input = Some(false);
 8384            });
 8385        })
 8386    });
 8387    cx.set_state("editorˇ");
 8388    cx.simulate_keystroke(".");
 8389    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8390    cx.simulate_keystroke("c");
 8391    cx.simulate_keystroke("l");
 8392    cx.simulate_keystroke("o");
 8393    cx.assert_editor_state("editor.cloˇ");
 8394    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8395    cx.update_editor(|editor, cx| {
 8396        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8397    });
 8398    handle_completion_request(
 8399        &mut cx,
 8400        "editor.<clo|>",
 8401        vec!["close", "clobber"],
 8402        counter.clone(),
 8403    )
 8404    .await;
 8405    cx.condition(|editor, _| editor.context_menu_visible())
 8406        .await;
 8407    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8408
 8409    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8410        editor
 8411            .confirm_completion(&ConfirmCompletion::default(), cx)
 8412            .unwrap()
 8413    });
 8414    cx.assert_editor_state("editor.closeˇ");
 8415    handle_resolve_completion_request(&mut cx, None).await;
 8416    apply_additional_edits.await.unwrap();
 8417}
 8418
 8419#[gpui::test]
 8420async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8421    init_test(cx, |_| {});
 8422    let mut cx = EditorLspTestContext::new_rust(
 8423        lsp::ServerCapabilities {
 8424            completion_provider: Some(lsp::CompletionOptions {
 8425                trigger_characters: Some(vec![".".to_string()]),
 8426                ..Default::default()
 8427            }),
 8428            ..Default::default()
 8429        },
 8430        cx,
 8431    )
 8432    .await;
 8433    cx.lsp
 8434        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8435            Ok(Some(lsp::CompletionResponse::Array(vec![
 8436                lsp::CompletionItem {
 8437                    label: "first".into(),
 8438                    ..Default::default()
 8439                },
 8440                lsp::CompletionItem {
 8441                    label: "last".into(),
 8442                    ..Default::default()
 8443                },
 8444            ])))
 8445        });
 8446    cx.set_state("variableˇ");
 8447    cx.simulate_keystroke(".");
 8448    cx.executor().run_until_parked();
 8449
 8450    cx.update_editor(|editor, _| {
 8451        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8452            assert_eq!(
 8453                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8454                &["first", "last"]
 8455            );
 8456        } else {
 8457            panic!("expected completion menu to be open");
 8458        }
 8459    });
 8460
 8461    cx.update_editor(|editor, cx| {
 8462        editor.move_page_down(&MovePageDown::default(), cx);
 8463        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8464            assert!(
 8465                menu.selected_item == 1,
 8466                "expected PageDown to select the last item from the context menu"
 8467            );
 8468        } else {
 8469            panic!("expected completion menu to stay open after PageDown");
 8470        }
 8471    });
 8472
 8473    cx.update_editor(|editor, cx| {
 8474        editor.move_page_up(&MovePageUp::default(), cx);
 8475        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8476            assert!(
 8477                menu.selected_item == 0,
 8478                "expected PageUp to select the first item from the context menu"
 8479            );
 8480        } else {
 8481            panic!("expected completion menu to stay open after PageUp");
 8482        }
 8483    });
 8484}
 8485
 8486#[gpui::test]
 8487async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8488    init_test(cx, |_| {});
 8489    let mut cx = EditorLspTestContext::new_rust(
 8490        lsp::ServerCapabilities {
 8491            completion_provider: Some(lsp::CompletionOptions {
 8492                trigger_characters: Some(vec![".".to_string()]),
 8493                ..Default::default()
 8494            }),
 8495            ..Default::default()
 8496        },
 8497        cx,
 8498    )
 8499    .await;
 8500    cx.lsp
 8501        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8502            Ok(Some(lsp::CompletionResponse::Array(vec![
 8503                lsp::CompletionItem {
 8504                    label: "Range".into(),
 8505                    sort_text: Some("a".into()),
 8506                    ..Default::default()
 8507                },
 8508                lsp::CompletionItem {
 8509                    label: "r".into(),
 8510                    sort_text: Some("b".into()),
 8511                    ..Default::default()
 8512                },
 8513                lsp::CompletionItem {
 8514                    label: "ret".into(),
 8515                    sort_text: Some("c".into()),
 8516                    ..Default::default()
 8517                },
 8518                lsp::CompletionItem {
 8519                    label: "return".into(),
 8520                    sort_text: Some("d".into()),
 8521                    ..Default::default()
 8522                },
 8523                lsp::CompletionItem {
 8524                    label: "slice".into(),
 8525                    sort_text: Some("d".into()),
 8526                    ..Default::default()
 8527                },
 8528            ])))
 8529        });
 8530    cx.set_state("");
 8531    cx.executor().run_until_parked();
 8532    cx.update_editor(|editor, cx| {
 8533        editor.show_completions(
 8534            &ShowCompletions {
 8535                trigger: Some("r".into()),
 8536            },
 8537            cx,
 8538        );
 8539    });
 8540    cx.executor().run_until_parked();
 8541
 8542    cx.update_editor(|editor, _| {
 8543        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8544            assert_eq!(
 8545                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8546                &["r", "ret", "Range", "return"]
 8547            );
 8548        } else {
 8549            panic!("expected completion menu to be open");
 8550        }
 8551    });
 8552}
 8553
 8554#[gpui::test]
 8555async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8556    init_test(cx, |_| {});
 8557
 8558    let mut cx = EditorLspTestContext::new_rust(
 8559        lsp::ServerCapabilities {
 8560            completion_provider: Some(lsp::CompletionOptions {
 8561                trigger_characters: Some(vec![".".to_string()]),
 8562                resolve_provider: Some(true),
 8563                ..Default::default()
 8564            }),
 8565            ..Default::default()
 8566        },
 8567        cx,
 8568    )
 8569    .await;
 8570
 8571    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8572    cx.simulate_keystroke(".");
 8573    let completion_item = lsp::CompletionItem {
 8574        label: "Some".into(),
 8575        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8576        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8577        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8578            kind: lsp::MarkupKind::Markdown,
 8579            value: "```rust\nSome(2)\n```".to_string(),
 8580        })),
 8581        deprecated: Some(false),
 8582        sort_text: Some("Some".to_string()),
 8583        filter_text: Some("Some".to_string()),
 8584        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8585        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8586            range: lsp::Range {
 8587                start: lsp::Position {
 8588                    line: 0,
 8589                    character: 22,
 8590                },
 8591                end: lsp::Position {
 8592                    line: 0,
 8593                    character: 22,
 8594                },
 8595            },
 8596            new_text: "Some(2)".to_string(),
 8597        })),
 8598        additional_text_edits: Some(vec![lsp::TextEdit {
 8599            range: lsp::Range {
 8600                start: lsp::Position {
 8601                    line: 0,
 8602                    character: 20,
 8603                },
 8604                end: lsp::Position {
 8605                    line: 0,
 8606                    character: 22,
 8607                },
 8608            },
 8609            new_text: "".to_string(),
 8610        }]),
 8611        ..Default::default()
 8612    };
 8613
 8614    let closure_completion_item = completion_item.clone();
 8615    let counter = Arc::new(AtomicUsize::new(0));
 8616    let counter_clone = counter.clone();
 8617    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8618        let task_completion_item = closure_completion_item.clone();
 8619        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8620        async move {
 8621            Ok(Some(lsp::CompletionResponse::Array(vec![
 8622                task_completion_item,
 8623            ])))
 8624        }
 8625    });
 8626
 8627    cx.condition(|editor, _| editor.context_menu_visible())
 8628        .await;
 8629    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8630    assert!(request.next().await.is_some());
 8631    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8632
 8633    cx.simulate_keystroke("S");
 8634    cx.simulate_keystroke("o");
 8635    cx.simulate_keystroke("m");
 8636    cx.condition(|editor, _| editor.context_menu_visible())
 8637        .await;
 8638    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8639    assert!(request.next().await.is_some());
 8640    assert!(request.next().await.is_some());
 8641    assert!(request.next().await.is_some());
 8642    request.close();
 8643    assert!(request.next().await.is_none());
 8644    assert_eq!(
 8645        counter.load(atomic::Ordering::Acquire),
 8646        4,
 8647        "With the completions menu open, only one LSP request should happen per input"
 8648    );
 8649}
 8650
 8651#[gpui::test]
 8652async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8653    init_test(cx, |_| {});
 8654    let mut cx = EditorTestContext::new(cx).await;
 8655    let language = Arc::new(Language::new(
 8656        LanguageConfig {
 8657            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8658            ..Default::default()
 8659        },
 8660        Some(tree_sitter_rust::LANGUAGE.into()),
 8661    ));
 8662    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8663
 8664    // If multiple selections intersect a line, the line is only toggled once.
 8665    cx.set_state(indoc! {"
 8666        fn a() {
 8667            «//b();
 8668            ˇ»// «c();
 8669            //ˇ»  d();
 8670        }
 8671    "});
 8672
 8673    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8674
 8675    cx.assert_editor_state(indoc! {"
 8676        fn a() {
 8677            «b();
 8678            c();
 8679            ˇ» d();
 8680        }
 8681    "});
 8682
 8683    // The comment prefix is inserted at the same column for every line in a
 8684    // selection.
 8685    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8686
 8687    cx.assert_editor_state(indoc! {"
 8688        fn a() {
 8689            // «b();
 8690            // c();
 8691            ˇ»//  d();
 8692        }
 8693    "});
 8694
 8695    // If a selection ends at the beginning of a line, that line is not toggled.
 8696    cx.set_selections_state(indoc! {"
 8697        fn a() {
 8698            // b();
 8699            «// c();
 8700        ˇ»    //  d();
 8701        }
 8702    "});
 8703
 8704    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8705
 8706    cx.assert_editor_state(indoc! {"
 8707        fn a() {
 8708            // b();
 8709            «c();
 8710        ˇ»    //  d();
 8711        }
 8712    "});
 8713
 8714    // If a selection span a single line and is empty, the line is toggled.
 8715    cx.set_state(indoc! {"
 8716        fn a() {
 8717            a();
 8718            b();
 8719        ˇ
 8720        }
 8721    "});
 8722
 8723    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8724
 8725    cx.assert_editor_state(indoc! {"
 8726        fn a() {
 8727            a();
 8728            b();
 8729        //•ˇ
 8730        }
 8731    "});
 8732
 8733    // If a selection span multiple lines, empty lines are not toggled.
 8734    cx.set_state(indoc! {"
 8735        fn a() {
 8736            «a();
 8737
 8738            c();ˇ»
 8739        }
 8740    "});
 8741
 8742    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8743
 8744    cx.assert_editor_state(indoc! {"
 8745        fn a() {
 8746            // «a();
 8747
 8748            // c();ˇ»
 8749        }
 8750    "});
 8751
 8752    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8753    cx.set_state(indoc! {"
 8754        fn a() {
 8755            «// a();
 8756            /// b();
 8757            //! c();ˇ»
 8758        }
 8759    "});
 8760
 8761    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8762
 8763    cx.assert_editor_state(indoc! {"
 8764        fn a() {
 8765            «a();
 8766            b();
 8767            c();ˇ»
 8768        }
 8769    "});
 8770}
 8771
 8772#[gpui::test]
 8773async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8774    init_test(cx, |_| {});
 8775    let mut cx = EditorTestContext::new(cx).await;
 8776    let language = Arc::new(Language::new(
 8777        LanguageConfig {
 8778            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8779            ..Default::default()
 8780        },
 8781        Some(tree_sitter_rust::LANGUAGE.into()),
 8782    ));
 8783    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8784
 8785    let toggle_comments = &ToggleComments {
 8786        advance_downwards: false,
 8787        ignore_indent: true,
 8788    };
 8789
 8790    // If multiple selections intersect a line, the line is only toggled once.
 8791    cx.set_state(indoc! {"
 8792        fn a() {
 8793        //    «b();
 8794        //    c();
 8795        //    ˇ» d();
 8796        }
 8797    "});
 8798
 8799    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8800
 8801    cx.assert_editor_state(indoc! {"
 8802        fn a() {
 8803            «b();
 8804            c();
 8805            ˇ» d();
 8806        }
 8807    "});
 8808
 8809    // The comment prefix is inserted at the beginning of each line
 8810    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8811
 8812    cx.assert_editor_state(indoc! {"
 8813        fn a() {
 8814        //    «b();
 8815        //    c();
 8816        //    ˇ» d();
 8817        }
 8818    "});
 8819
 8820    // If a selection ends at the beginning of a line, that line is not toggled.
 8821    cx.set_selections_state(indoc! {"
 8822        fn a() {
 8823        //    b();
 8824        //    «c();
 8825        ˇ»//     d();
 8826        }
 8827    "});
 8828
 8829    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8830
 8831    cx.assert_editor_state(indoc! {"
 8832        fn a() {
 8833        //    b();
 8834            «c();
 8835        ˇ»//     d();
 8836        }
 8837    "});
 8838
 8839    // If a selection span a single line and is empty, the line is toggled.
 8840    cx.set_state(indoc! {"
 8841        fn a() {
 8842            a();
 8843            b();
 8844        ˇ
 8845        }
 8846    "});
 8847
 8848    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8849
 8850    cx.assert_editor_state(indoc! {"
 8851        fn a() {
 8852            a();
 8853            b();
 8854        //ˇ
 8855        }
 8856    "});
 8857
 8858    // If a selection span multiple lines, empty lines are not toggled.
 8859    cx.set_state(indoc! {"
 8860        fn a() {
 8861            «a();
 8862
 8863            c();ˇ»
 8864        }
 8865    "});
 8866
 8867    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8868
 8869    cx.assert_editor_state(indoc! {"
 8870        fn a() {
 8871        //    «a();
 8872
 8873        //    c();ˇ»
 8874        }
 8875    "});
 8876
 8877    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8878    cx.set_state(indoc! {"
 8879        fn a() {
 8880        //    «a();
 8881        ///    b();
 8882        //!    c();ˇ»
 8883        }
 8884    "});
 8885
 8886    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8887
 8888    cx.assert_editor_state(indoc! {"
 8889        fn a() {
 8890            «a();
 8891            b();
 8892            c();ˇ»
 8893        }
 8894    "});
 8895}
 8896
 8897#[gpui::test]
 8898async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8899    init_test(cx, |_| {});
 8900
 8901    let language = Arc::new(Language::new(
 8902        LanguageConfig {
 8903            line_comments: vec!["// ".into()],
 8904            ..Default::default()
 8905        },
 8906        Some(tree_sitter_rust::LANGUAGE.into()),
 8907    ));
 8908
 8909    let mut cx = EditorTestContext::new(cx).await;
 8910
 8911    cx.language_registry().add(language.clone());
 8912    cx.update_buffer(|buffer, cx| {
 8913        buffer.set_language(Some(language), cx);
 8914    });
 8915
 8916    let toggle_comments = &ToggleComments {
 8917        advance_downwards: true,
 8918        ignore_indent: false,
 8919    };
 8920
 8921    // Single cursor on one line -> advance
 8922    // Cursor moves horizontally 3 characters as well on non-blank line
 8923    cx.set_state(indoc!(
 8924        "fn a() {
 8925             ˇdog();
 8926             cat();
 8927        }"
 8928    ));
 8929    cx.update_editor(|editor, cx| {
 8930        editor.toggle_comments(toggle_comments, cx);
 8931    });
 8932    cx.assert_editor_state(indoc!(
 8933        "fn a() {
 8934             // dog();
 8935             catˇ();
 8936        }"
 8937    ));
 8938
 8939    // Single selection on one line -> don't advance
 8940    cx.set_state(indoc!(
 8941        "fn a() {
 8942             «dog()ˇ»;
 8943             cat();
 8944        }"
 8945    ));
 8946    cx.update_editor(|editor, cx| {
 8947        editor.toggle_comments(toggle_comments, cx);
 8948    });
 8949    cx.assert_editor_state(indoc!(
 8950        "fn a() {
 8951             // «dog()ˇ»;
 8952             cat();
 8953        }"
 8954    ));
 8955
 8956    // Multiple cursors on one line -> advance
 8957    cx.set_state(indoc!(
 8958        "fn a() {
 8959             ˇdˇog();
 8960             cat();
 8961        }"
 8962    ));
 8963    cx.update_editor(|editor, cx| {
 8964        editor.toggle_comments(toggle_comments, cx);
 8965    });
 8966    cx.assert_editor_state(indoc!(
 8967        "fn a() {
 8968             // dog();
 8969             catˇ(ˇ);
 8970        }"
 8971    ));
 8972
 8973    // Multiple cursors on one line, with selection -> don't advance
 8974    cx.set_state(indoc!(
 8975        "fn a() {
 8976             ˇdˇog«()ˇ»;
 8977             cat();
 8978        }"
 8979    ));
 8980    cx.update_editor(|editor, cx| {
 8981        editor.toggle_comments(toggle_comments, cx);
 8982    });
 8983    cx.assert_editor_state(indoc!(
 8984        "fn a() {
 8985             // ˇdˇog«()ˇ»;
 8986             cat();
 8987        }"
 8988    ));
 8989
 8990    // Single cursor on one line -> advance
 8991    // Cursor moves to column 0 on blank line
 8992    cx.set_state(indoc!(
 8993        "fn a() {
 8994             ˇdog();
 8995
 8996             cat();
 8997        }"
 8998    ));
 8999    cx.update_editor(|editor, cx| {
 9000        editor.toggle_comments(toggle_comments, cx);
 9001    });
 9002    cx.assert_editor_state(indoc!(
 9003        "fn a() {
 9004             // dog();
 9005        ˇ
 9006             cat();
 9007        }"
 9008    ));
 9009
 9010    // Single cursor on one line -> advance
 9011    // Cursor starts and ends at column 0
 9012    cx.set_state(indoc!(
 9013        "fn a() {
 9014         ˇ    dog();
 9015             cat();
 9016        }"
 9017    ));
 9018    cx.update_editor(|editor, cx| {
 9019        editor.toggle_comments(toggle_comments, cx);
 9020    });
 9021    cx.assert_editor_state(indoc!(
 9022        "fn a() {
 9023             // dog();
 9024         ˇ    cat();
 9025        }"
 9026    ));
 9027}
 9028
 9029#[gpui::test]
 9030async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9031    init_test(cx, |_| {});
 9032
 9033    let mut cx = EditorTestContext::new(cx).await;
 9034
 9035    let html_language = Arc::new(
 9036        Language::new(
 9037            LanguageConfig {
 9038                name: "HTML".into(),
 9039                block_comment: Some(("<!-- ".into(), " -->".into())),
 9040                ..Default::default()
 9041            },
 9042            Some(tree_sitter_html::language()),
 9043        )
 9044        .with_injection_query(
 9045            r#"
 9046            (script_element
 9047                (raw_text) @content
 9048                (#set! "language" "javascript"))
 9049            "#,
 9050        )
 9051        .unwrap(),
 9052    );
 9053
 9054    let javascript_language = Arc::new(Language::new(
 9055        LanguageConfig {
 9056            name: "JavaScript".into(),
 9057            line_comments: vec!["// ".into()],
 9058            ..Default::default()
 9059        },
 9060        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9061    ));
 9062
 9063    cx.language_registry().add(html_language.clone());
 9064    cx.language_registry().add(javascript_language.clone());
 9065    cx.update_buffer(|buffer, cx| {
 9066        buffer.set_language(Some(html_language), cx);
 9067    });
 9068
 9069    // Toggle comments for empty selections
 9070    cx.set_state(
 9071        &r#"
 9072            <p>A</p>ˇ
 9073            <p>B</p>ˇ
 9074            <p>C</p>ˇ
 9075        "#
 9076        .unindent(),
 9077    );
 9078    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9079    cx.assert_editor_state(
 9080        &r#"
 9081            <!-- <p>A</p>ˇ -->
 9082            <!-- <p>B</p>ˇ -->
 9083            <!-- <p>C</p>ˇ -->
 9084        "#
 9085        .unindent(),
 9086    );
 9087    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9088    cx.assert_editor_state(
 9089        &r#"
 9090            <p>A</p>ˇ
 9091            <p>B</p>ˇ
 9092            <p>C</p>ˇ
 9093        "#
 9094        .unindent(),
 9095    );
 9096
 9097    // Toggle comments for mixture of empty and non-empty selections, where
 9098    // multiple selections occupy a given line.
 9099    cx.set_state(
 9100        &r#"
 9101            <p>A«</p>
 9102            <p>ˇ»B</p>ˇ
 9103            <p>C«</p>
 9104            <p>ˇ»D</p>ˇ
 9105        "#
 9106        .unindent(),
 9107    );
 9108
 9109    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9110    cx.assert_editor_state(
 9111        &r#"
 9112            <!-- <p>A«</p>
 9113            <p>ˇ»B</p>ˇ -->
 9114            <!-- <p>C«</p>
 9115            <p>ˇ»D</p>ˇ -->
 9116        "#
 9117        .unindent(),
 9118    );
 9119    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9120    cx.assert_editor_state(
 9121        &r#"
 9122            <p>A«</p>
 9123            <p>ˇ»B</p>ˇ
 9124            <p>C«</p>
 9125            <p>ˇ»D</p>ˇ
 9126        "#
 9127        .unindent(),
 9128    );
 9129
 9130    // Toggle comments when different languages are active for different
 9131    // selections.
 9132    cx.set_state(
 9133        &r#"
 9134            ˇ<script>
 9135                ˇvar x = new Y();
 9136            ˇ</script>
 9137        "#
 9138        .unindent(),
 9139    );
 9140    cx.executor().run_until_parked();
 9141    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9142    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9143    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9144    cx.assert_editor_state(
 9145        &r#"
 9146            <!-- ˇ<script> -->
 9147                // ˇvar x = new Y();
 9148            // ˇ</script>
 9149        "#
 9150        .unindent(),
 9151    );
 9152}
 9153
 9154#[gpui::test]
 9155fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9156    init_test(cx, |_| {});
 9157
 9158    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9159    let multibuffer = cx.new_model(|cx| {
 9160        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9161        multibuffer.push_excerpts(
 9162            buffer.clone(),
 9163            [
 9164                ExcerptRange {
 9165                    context: Point::new(0, 0)..Point::new(0, 4),
 9166                    primary: None,
 9167                },
 9168                ExcerptRange {
 9169                    context: Point::new(1, 0)..Point::new(1, 4),
 9170                    primary: None,
 9171                },
 9172            ],
 9173            cx,
 9174        );
 9175        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9176        multibuffer
 9177    });
 9178
 9179    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9180    view.update(cx, |view, cx| {
 9181        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9182        view.change_selections(None, cx, |s| {
 9183            s.select_ranges([
 9184                Point::new(0, 0)..Point::new(0, 0),
 9185                Point::new(1, 0)..Point::new(1, 0),
 9186            ])
 9187        });
 9188
 9189        view.handle_input("X", cx);
 9190        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9191        assert_eq!(
 9192            view.selections.ranges(cx),
 9193            [
 9194                Point::new(0, 1)..Point::new(0, 1),
 9195                Point::new(1, 1)..Point::new(1, 1),
 9196            ]
 9197        );
 9198
 9199        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9200        view.change_selections(None, cx, |s| {
 9201            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9202        });
 9203        view.backspace(&Default::default(), cx);
 9204        assert_eq!(view.text(cx), "Xa\nbbb");
 9205        assert_eq!(
 9206            view.selections.ranges(cx),
 9207            [Point::new(1, 0)..Point::new(1, 0)]
 9208        );
 9209
 9210        view.change_selections(None, cx, |s| {
 9211            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9212        });
 9213        view.backspace(&Default::default(), cx);
 9214        assert_eq!(view.text(cx), "X\nbb");
 9215        assert_eq!(
 9216            view.selections.ranges(cx),
 9217            [Point::new(0, 1)..Point::new(0, 1)]
 9218        );
 9219    });
 9220}
 9221
 9222#[gpui::test]
 9223fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9224    init_test(cx, |_| {});
 9225
 9226    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9227    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9228        indoc! {"
 9229            [aaaa
 9230            (bbbb]
 9231            cccc)",
 9232        },
 9233        markers.clone(),
 9234    );
 9235    let excerpt_ranges = markers.into_iter().map(|marker| {
 9236        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9237        ExcerptRange {
 9238            context,
 9239            primary: None,
 9240        }
 9241    });
 9242    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9243    let multibuffer = cx.new_model(|cx| {
 9244        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9245        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9246        multibuffer
 9247    });
 9248
 9249    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9250    view.update(cx, |view, cx| {
 9251        let (expected_text, selection_ranges) = marked_text_ranges(
 9252            indoc! {"
 9253                aaaa
 9254                bˇbbb
 9255                bˇbbˇb
 9256                cccc"
 9257            },
 9258            true,
 9259        );
 9260        assert_eq!(view.text(cx), expected_text);
 9261        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9262
 9263        view.handle_input("X", cx);
 9264
 9265        let (expected_text, expected_selections) = marked_text_ranges(
 9266            indoc! {"
 9267                aaaa
 9268                bXˇbbXb
 9269                bXˇbbXˇb
 9270                cccc"
 9271            },
 9272            false,
 9273        );
 9274        assert_eq!(view.text(cx), expected_text);
 9275        assert_eq!(view.selections.ranges(cx), expected_selections);
 9276
 9277        view.newline(&Newline, cx);
 9278        let (expected_text, expected_selections) = marked_text_ranges(
 9279            indoc! {"
 9280                aaaa
 9281                bX
 9282                ˇbbX
 9283                b
 9284                bX
 9285                ˇbbX
 9286                ˇb
 9287                cccc"
 9288            },
 9289            false,
 9290        );
 9291        assert_eq!(view.text(cx), expected_text);
 9292        assert_eq!(view.selections.ranges(cx), expected_selections);
 9293    });
 9294}
 9295
 9296#[gpui::test]
 9297fn test_refresh_selections(cx: &mut TestAppContext) {
 9298    init_test(cx, |_| {});
 9299
 9300    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9301    let mut excerpt1_id = None;
 9302    let multibuffer = cx.new_model(|cx| {
 9303        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9304        excerpt1_id = multibuffer
 9305            .push_excerpts(
 9306                buffer.clone(),
 9307                [
 9308                    ExcerptRange {
 9309                        context: Point::new(0, 0)..Point::new(1, 4),
 9310                        primary: None,
 9311                    },
 9312                    ExcerptRange {
 9313                        context: Point::new(1, 0)..Point::new(2, 4),
 9314                        primary: None,
 9315                    },
 9316                ],
 9317                cx,
 9318            )
 9319            .into_iter()
 9320            .next();
 9321        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9322        multibuffer
 9323    });
 9324
 9325    let editor = cx.add_window(|cx| {
 9326        let mut editor = build_editor(multibuffer.clone(), cx);
 9327        let snapshot = editor.snapshot(cx);
 9328        editor.change_selections(None, cx, |s| {
 9329            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9330        });
 9331        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9332        assert_eq!(
 9333            editor.selections.ranges(cx),
 9334            [
 9335                Point::new(1, 3)..Point::new(1, 3),
 9336                Point::new(2, 1)..Point::new(2, 1),
 9337            ]
 9338        );
 9339        editor
 9340    });
 9341
 9342    // Refreshing selections is a no-op when excerpts haven't changed.
 9343    _ = editor.update(cx, |editor, cx| {
 9344        editor.change_selections(None, cx, |s| s.refresh());
 9345        assert_eq!(
 9346            editor.selections.ranges(cx),
 9347            [
 9348                Point::new(1, 3)..Point::new(1, 3),
 9349                Point::new(2, 1)..Point::new(2, 1),
 9350            ]
 9351        );
 9352    });
 9353
 9354    multibuffer.update(cx, |multibuffer, cx| {
 9355        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9356    });
 9357    _ = editor.update(cx, |editor, cx| {
 9358        // Removing an excerpt causes the first selection to become degenerate.
 9359        assert_eq!(
 9360            editor.selections.ranges(cx),
 9361            [
 9362                Point::new(0, 0)..Point::new(0, 0),
 9363                Point::new(0, 1)..Point::new(0, 1)
 9364            ]
 9365        );
 9366
 9367        // Refreshing selections will relocate the first selection to the original buffer
 9368        // location.
 9369        editor.change_selections(None, cx, |s| s.refresh());
 9370        assert_eq!(
 9371            editor.selections.ranges(cx),
 9372            [
 9373                Point::new(0, 1)..Point::new(0, 1),
 9374                Point::new(0, 3)..Point::new(0, 3)
 9375            ]
 9376        );
 9377        assert!(editor.selections.pending_anchor().is_some());
 9378    });
 9379}
 9380
 9381#[gpui::test]
 9382fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9383    init_test(cx, |_| {});
 9384
 9385    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9386    let mut excerpt1_id = None;
 9387    let multibuffer = cx.new_model(|cx| {
 9388        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9389        excerpt1_id = multibuffer
 9390            .push_excerpts(
 9391                buffer.clone(),
 9392                [
 9393                    ExcerptRange {
 9394                        context: Point::new(0, 0)..Point::new(1, 4),
 9395                        primary: None,
 9396                    },
 9397                    ExcerptRange {
 9398                        context: Point::new(1, 0)..Point::new(2, 4),
 9399                        primary: None,
 9400                    },
 9401                ],
 9402                cx,
 9403            )
 9404            .into_iter()
 9405            .next();
 9406        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9407        multibuffer
 9408    });
 9409
 9410    let editor = cx.add_window(|cx| {
 9411        let mut editor = build_editor(multibuffer.clone(), cx);
 9412        let snapshot = editor.snapshot(cx);
 9413        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9414        assert_eq!(
 9415            editor.selections.ranges(cx),
 9416            [Point::new(1, 3)..Point::new(1, 3)]
 9417        );
 9418        editor
 9419    });
 9420
 9421    multibuffer.update(cx, |multibuffer, cx| {
 9422        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9423    });
 9424    _ = editor.update(cx, |editor, cx| {
 9425        assert_eq!(
 9426            editor.selections.ranges(cx),
 9427            [Point::new(0, 0)..Point::new(0, 0)]
 9428        );
 9429
 9430        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9431        editor.change_selections(None, cx, |s| s.refresh());
 9432        assert_eq!(
 9433            editor.selections.ranges(cx),
 9434            [Point::new(0, 3)..Point::new(0, 3)]
 9435        );
 9436        assert!(editor.selections.pending_anchor().is_some());
 9437    });
 9438}
 9439
 9440#[gpui::test]
 9441async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9442    init_test(cx, |_| {});
 9443
 9444    let language = Arc::new(
 9445        Language::new(
 9446            LanguageConfig {
 9447                brackets: BracketPairConfig {
 9448                    pairs: vec![
 9449                        BracketPair {
 9450                            start: "{".to_string(),
 9451                            end: "}".to_string(),
 9452                            close: true,
 9453                            surround: true,
 9454                            newline: true,
 9455                        },
 9456                        BracketPair {
 9457                            start: "/* ".to_string(),
 9458                            end: " */".to_string(),
 9459                            close: true,
 9460                            surround: true,
 9461                            newline: true,
 9462                        },
 9463                    ],
 9464                    ..Default::default()
 9465                },
 9466                ..Default::default()
 9467            },
 9468            Some(tree_sitter_rust::LANGUAGE.into()),
 9469        )
 9470        .with_indents_query("")
 9471        .unwrap(),
 9472    );
 9473
 9474    let text = concat!(
 9475        "{   }\n",     //
 9476        "  x\n",       //
 9477        "  /*   */\n", //
 9478        "x\n",         //
 9479        "{{} }\n",     //
 9480    );
 9481
 9482    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9483    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9484    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9485    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9486        .await;
 9487
 9488    view.update(cx, |view, cx| {
 9489        view.change_selections(None, cx, |s| {
 9490            s.select_display_ranges([
 9491                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9492                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9493                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9494            ])
 9495        });
 9496        view.newline(&Newline, cx);
 9497
 9498        assert_eq!(
 9499            view.buffer().read(cx).read(cx).text(),
 9500            concat!(
 9501                "{ \n",    // Suppress rustfmt
 9502                "\n",      //
 9503                "}\n",     //
 9504                "  x\n",   //
 9505                "  /* \n", //
 9506                "  \n",    //
 9507                "  */\n",  //
 9508                "x\n",     //
 9509                "{{} \n",  //
 9510                "}\n",     //
 9511            )
 9512        );
 9513    });
 9514}
 9515
 9516#[gpui::test]
 9517fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9518    init_test(cx, |_| {});
 9519
 9520    let editor = cx.add_window(|cx| {
 9521        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9522        build_editor(buffer.clone(), cx)
 9523    });
 9524
 9525    _ = editor.update(cx, |editor, cx| {
 9526        struct Type1;
 9527        struct Type2;
 9528
 9529        let buffer = editor.buffer.read(cx).snapshot(cx);
 9530
 9531        let anchor_range =
 9532            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9533
 9534        editor.highlight_background::<Type1>(
 9535            &[
 9536                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9537                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9538                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9539                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9540            ],
 9541            |_| Hsla::red(),
 9542            cx,
 9543        );
 9544        editor.highlight_background::<Type2>(
 9545            &[
 9546                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9547                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9548                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9549                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9550            ],
 9551            |_| Hsla::green(),
 9552            cx,
 9553        );
 9554
 9555        let snapshot = editor.snapshot(cx);
 9556        let mut highlighted_ranges = editor.background_highlights_in_range(
 9557            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9558            &snapshot,
 9559            cx.theme().colors(),
 9560        );
 9561        // Enforce a consistent ordering based on color without relying on the ordering of the
 9562        // highlight's `TypeId` which is non-executor.
 9563        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9564        assert_eq!(
 9565            highlighted_ranges,
 9566            &[
 9567                (
 9568                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9569                    Hsla::red(),
 9570                ),
 9571                (
 9572                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9573                    Hsla::red(),
 9574                ),
 9575                (
 9576                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9577                    Hsla::green(),
 9578                ),
 9579                (
 9580                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9581                    Hsla::green(),
 9582                ),
 9583            ]
 9584        );
 9585        assert_eq!(
 9586            editor.background_highlights_in_range(
 9587                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9588                &snapshot,
 9589                cx.theme().colors(),
 9590            ),
 9591            &[(
 9592                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9593                Hsla::red(),
 9594            )]
 9595        );
 9596    });
 9597}
 9598
 9599#[gpui::test]
 9600async fn test_following(cx: &mut gpui::TestAppContext) {
 9601    init_test(cx, |_| {});
 9602
 9603    let fs = FakeFs::new(cx.executor());
 9604    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9605
 9606    let buffer = project.update(cx, |project, cx| {
 9607        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9608        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9609    });
 9610    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9611    let follower = cx.update(|cx| {
 9612        cx.open_window(
 9613            WindowOptions {
 9614                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9615                    gpui::Point::new(px(0.), px(0.)),
 9616                    gpui::Point::new(px(10.), px(80.)),
 9617                ))),
 9618                ..Default::default()
 9619            },
 9620            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9621        )
 9622        .unwrap()
 9623    });
 9624
 9625    let is_still_following = Rc::new(RefCell::new(true));
 9626    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9627    let pending_update = Rc::new(RefCell::new(None));
 9628    _ = follower.update(cx, {
 9629        let update = pending_update.clone();
 9630        let is_still_following = is_still_following.clone();
 9631        let follower_edit_event_count = follower_edit_event_count.clone();
 9632        |_, cx| {
 9633            cx.subscribe(
 9634                &leader.root_view(cx).unwrap(),
 9635                move |_, leader, event, cx| {
 9636                    leader
 9637                        .read(cx)
 9638                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9639                },
 9640            )
 9641            .detach();
 9642
 9643            cx.subscribe(
 9644                &follower.root_view(cx).unwrap(),
 9645                move |_, _, event: &EditorEvent, _cx| {
 9646                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9647                        *is_still_following.borrow_mut() = false;
 9648                    }
 9649
 9650                    if let EditorEvent::BufferEdited = event {
 9651                        *follower_edit_event_count.borrow_mut() += 1;
 9652                    }
 9653                },
 9654            )
 9655            .detach();
 9656        }
 9657    });
 9658
 9659    // Update the selections only
 9660    _ = leader.update(cx, |leader, cx| {
 9661        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9662    });
 9663    follower
 9664        .update(cx, |follower, cx| {
 9665            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9666        })
 9667        .unwrap()
 9668        .await
 9669        .unwrap();
 9670    _ = follower.update(cx, |follower, cx| {
 9671        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9672    });
 9673    assert!(*is_still_following.borrow());
 9674    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9675
 9676    // Update the scroll position only
 9677    _ = leader.update(cx, |leader, cx| {
 9678        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9679    });
 9680    follower
 9681        .update(cx, |follower, cx| {
 9682            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9683        })
 9684        .unwrap()
 9685        .await
 9686        .unwrap();
 9687    assert_eq!(
 9688        follower
 9689            .update(cx, |follower, cx| follower.scroll_position(cx))
 9690            .unwrap(),
 9691        gpui::Point::new(1.5, 3.5)
 9692    );
 9693    assert!(*is_still_following.borrow());
 9694    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9695
 9696    // Update the selections and scroll position. The follower's scroll position is updated
 9697    // via autoscroll, not via the leader's exact scroll position.
 9698    _ = leader.update(cx, |leader, cx| {
 9699        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9700        leader.request_autoscroll(Autoscroll::newest(), cx);
 9701        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9702    });
 9703    follower
 9704        .update(cx, |follower, cx| {
 9705            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9706        })
 9707        .unwrap()
 9708        .await
 9709        .unwrap();
 9710    _ = follower.update(cx, |follower, cx| {
 9711        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9712        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9713    });
 9714    assert!(*is_still_following.borrow());
 9715
 9716    // Creating a pending selection that precedes another selection
 9717    _ = leader.update(cx, |leader, cx| {
 9718        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9719        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9720    });
 9721    follower
 9722        .update(cx, |follower, cx| {
 9723            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9724        })
 9725        .unwrap()
 9726        .await
 9727        .unwrap();
 9728    _ = follower.update(cx, |follower, cx| {
 9729        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9730    });
 9731    assert!(*is_still_following.borrow());
 9732
 9733    // Extend the pending selection so that it surrounds another selection
 9734    _ = leader.update(cx, |leader, cx| {
 9735        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9736    });
 9737    follower
 9738        .update(cx, |follower, cx| {
 9739            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9740        })
 9741        .unwrap()
 9742        .await
 9743        .unwrap();
 9744    _ = follower.update(cx, |follower, cx| {
 9745        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9746    });
 9747
 9748    // Scrolling locally breaks the follow
 9749    _ = follower.update(cx, |follower, cx| {
 9750        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9751        follower.set_scroll_anchor(
 9752            ScrollAnchor {
 9753                anchor: top_anchor,
 9754                offset: gpui::Point::new(0.0, 0.5),
 9755            },
 9756            cx,
 9757        );
 9758    });
 9759    assert!(!(*is_still_following.borrow()));
 9760}
 9761
 9762#[gpui::test]
 9763async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9764    init_test(cx, |_| {});
 9765
 9766    let fs = FakeFs::new(cx.executor());
 9767    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9768    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9769    let pane = workspace
 9770        .update(cx, |workspace, _| workspace.active_pane().clone())
 9771        .unwrap();
 9772
 9773    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9774
 9775    let leader = pane.update(cx, |_, cx| {
 9776        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9777        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9778    });
 9779
 9780    // Start following the editor when it has no excerpts.
 9781    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9782    let follower_1 = cx
 9783        .update_window(*workspace.deref(), |_, cx| {
 9784            Editor::from_state_proto(
 9785                workspace.root_view(cx).unwrap(),
 9786                ViewId {
 9787                    creator: Default::default(),
 9788                    id: 0,
 9789                },
 9790                &mut state_message,
 9791                cx,
 9792            )
 9793        })
 9794        .unwrap()
 9795        .unwrap()
 9796        .await
 9797        .unwrap();
 9798
 9799    let update_message = Rc::new(RefCell::new(None));
 9800    follower_1.update(cx, {
 9801        let update = update_message.clone();
 9802        |_, cx| {
 9803            cx.subscribe(&leader, move |_, leader, event, cx| {
 9804                leader
 9805                    .read(cx)
 9806                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9807            })
 9808            .detach();
 9809        }
 9810    });
 9811
 9812    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9813        (
 9814            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9815            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9816        )
 9817    });
 9818
 9819    // Insert some excerpts.
 9820    leader.update(cx, |leader, cx| {
 9821        leader.buffer.update(cx, |multibuffer, cx| {
 9822            let excerpt_ids = multibuffer.push_excerpts(
 9823                buffer_1.clone(),
 9824                [
 9825                    ExcerptRange {
 9826                        context: 1..6,
 9827                        primary: None,
 9828                    },
 9829                    ExcerptRange {
 9830                        context: 12..15,
 9831                        primary: None,
 9832                    },
 9833                    ExcerptRange {
 9834                        context: 0..3,
 9835                        primary: None,
 9836                    },
 9837                ],
 9838                cx,
 9839            );
 9840            multibuffer.insert_excerpts_after(
 9841                excerpt_ids[0],
 9842                buffer_2.clone(),
 9843                [
 9844                    ExcerptRange {
 9845                        context: 8..12,
 9846                        primary: None,
 9847                    },
 9848                    ExcerptRange {
 9849                        context: 0..6,
 9850                        primary: None,
 9851                    },
 9852                ],
 9853                cx,
 9854            );
 9855        });
 9856    });
 9857
 9858    // Apply the update of adding the excerpts.
 9859    follower_1
 9860        .update(cx, |follower, cx| {
 9861            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9862        })
 9863        .await
 9864        .unwrap();
 9865    assert_eq!(
 9866        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9867        leader.update(cx, |editor, cx| editor.text(cx))
 9868    );
 9869    update_message.borrow_mut().take();
 9870
 9871    // Start following separately after it already has excerpts.
 9872    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9873    let follower_2 = cx
 9874        .update_window(*workspace.deref(), |_, cx| {
 9875            Editor::from_state_proto(
 9876                workspace.root_view(cx).unwrap().clone(),
 9877                ViewId {
 9878                    creator: Default::default(),
 9879                    id: 0,
 9880                },
 9881                &mut state_message,
 9882                cx,
 9883            )
 9884        })
 9885        .unwrap()
 9886        .unwrap()
 9887        .await
 9888        .unwrap();
 9889    assert_eq!(
 9890        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9891        leader.update(cx, |editor, cx| editor.text(cx))
 9892    );
 9893
 9894    // Remove some excerpts.
 9895    leader.update(cx, |leader, cx| {
 9896        leader.buffer.update(cx, |multibuffer, cx| {
 9897            let excerpt_ids = multibuffer.excerpt_ids();
 9898            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9899            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9900        });
 9901    });
 9902
 9903    // Apply the update of removing the excerpts.
 9904    follower_1
 9905        .update(cx, |follower, cx| {
 9906            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9907        })
 9908        .await
 9909        .unwrap();
 9910    follower_2
 9911        .update(cx, |follower, cx| {
 9912            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9913        })
 9914        .await
 9915        .unwrap();
 9916    update_message.borrow_mut().take();
 9917    assert_eq!(
 9918        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9919        leader.update(cx, |editor, cx| editor.text(cx))
 9920    );
 9921}
 9922
 9923#[gpui::test]
 9924async fn go_to_prev_overlapping_diagnostic(
 9925    executor: BackgroundExecutor,
 9926    cx: &mut gpui::TestAppContext,
 9927) {
 9928    init_test(cx, |_| {});
 9929
 9930    let mut cx = EditorTestContext::new(cx).await;
 9931    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9932
 9933    cx.set_state(indoc! {"
 9934        ˇfn func(abc def: i32) -> u32 {
 9935        }
 9936    "});
 9937
 9938    cx.update(|cx| {
 9939        project.update(cx, |project, cx| {
 9940            project
 9941                .update_diagnostics(
 9942                    LanguageServerId(0),
 9943                    lsp::PublishDiagnosticsParams {
 9944                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9945                        version: None,
 9946                        diagnostics: vec![
 9947                            lsp::Diagnostic {
 9948                                range: lsp::Range::new(
 9949                                    lsp::Position::new(0, 11),
 9950                                    lsp::Position::new(0, 12),
 9951                                ),
 9952                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9953                                ..Default::default()
 9954                            },
 9955                            lsp::Diagnostic {
 9956                                range: lsp::Range::new(
 9957                                    lsp::Position::new(0, 12),
 9958                                    lsp::Position::new(0, 15),
 9959                                ),
 9960                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9961                                ..Default::default()
 9962                            },
 9963                            lsp::Diagnostic {
 9964                                range: lsp::Range::new(
 9965                                    lsp::Position::new(0, 25),
 9966                                    lsp::Position::new(0, 28),
 9967                                ),
 9968                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9969                                ..Default::default()
 9970                            },
 9971                        ],
 9972                    },
 9973                    &[],
 9974                    cx,
 9975                )
 9976                .unwrap()
 9977        });
 9978    });
 9979
 9980    executor.run_until_parked();
 9981
 9982    cx.update_editor(|editor, cx| {
 9983        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9984    });
 9985
 9986    cx.assert_editor_state(indoc! {"
 9987        fn func(abc def: i32) -> ˇu32 {
 9988        }
 9989    "});
 9990
 9991    cx.update_editor(|editor, cx| {
 9992        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9993    });
 9994
 9995    cx.assert_editor_state(indoc! {"
 9996        fn func(abc ˇdef: i32) -> u32 {
 9997        }
 9998    "});
 9999
10000    cx.update_editor(|editor, cx| {
10001        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10002    });
10003
10004    cx.assert_editor_state(indoc! {"
10005        fn func(abcˇ def: i32) -> u32 {
10006        }
10007    "});
10008
10009    cx.update_editor(|editor, cx| {
10010        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10011    });
10012
10013    cx.assert_editor_state(indoc! {"
10014        fn func(abc def: i32) -> ˇu32 {
10015        }
10016    "});
10017}
10018
10019#[gpui::test]
10020async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10021    init_test(cx, |_| {});
10022
10023    let mut cx = EditorTestContext::new(cx).await;
10024
10025    cx.set_state(indoc! {"
10026        fn func(abˇc def: i32) -> u32 {
10027        }
10028    "});
10029    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
10030
10031    cx.update(|cx| {
10032        project.update(cx, |project, cx| {
10033            project.update_diagnostics(
10034                LanguageServerId(0),
10035                lsp::PublishDiagnosticsParams {
10036                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10037                    version: None,
10038                    diagnostics: vec![lsp::Diagnostic {
10039                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10040                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10041                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10042                        ..Default::default()
10043                    }],
10044                },
10045                &[],
10046                cx,
10047            )
10048        })
10049    }).unwrap();
10050    cx.run_until_parked();
10051    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10052    cx.run_until_parked();
10053    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10054}
10055
10056#[gpui::test]
10057async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10058    init_test(cx, |_| {});
10059
10060    let mut cx = EditorTestContext::new(cx).await;
10061
10062    let diff_base = r#"
10063        use some::mod;
10064
10065        const A: u32 = 42;
10066
10067        fn main() {
10068            println!("hello");
10069
10070            println!("world");
10071        }
10072        "#
10073    .unindent();
10074
10075    // Edits are modified, removed, modified, added
10076    cx.set_state(
10077        &r#"
10078        use some::modified;
10079
10080        ˇ
10081        fn main() {
10082            println!("hello there");
10083
10084            println!("around the");
10085            println!("world");
10086        }
10087        "#
10088        .unindent(),
10089    );
10090
10091    cx.set_diff_base(Some(&diff_base));
10092    executor.run_until_parked();
10093
10094    cx.update_editor(|editor, cx| {
10095        //Wrap around the bottom of the buffer
10096        for _ in 0..3 {
10097            editor.go_to_next_hunk(&GoToHunk, cx);
10098        }
10099    });
10100
10101    cx.assert_editor_state(
10102        &r#"
10103        ˇuse some::modified;
10104
10105
10106        fn main() {
10107            println!("hello there");
10108
10109            println!("around the");
10110            println!("world");
10111        }
10112        "#
10113        .unindent(),
10114    );
10115
10116    cx.update_editor(|editor, cx| {
10117        //Wrap around the top of the buffer
10118        for _ in 0..2 {
10119            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10120        }
10121    });
10122
10123    cx.assert_editor_state(
10124        &r#"
10125        use some::modified;
10126
10127
10128        fn main() {
10129        ˇ    println!("hello there");
10130
10131            println!("around the");
10132            println!("world");
10133        }
10134        "#
10135        .unindent(),
10136    );
10137
10138    cx.update_editor(|editor, cx| {
10139        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10140    });
10141
10142    cx.assert_editor_state(
10143        &r#"
10144        use some::modified;
10145
10146        ˇ
10147        fn main() {
10148            println!("hello there");
10149
10150            println!("around the");
10151            println!("world");
10152        }
10153        "#
10154        .unindent(),
10155    );
10156
10157    cx.update_editor(|editor, cx| {
10158        for _ in 0..3 {
10159            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10160        }
10161    });
10162
10163    cx.assert_editor_state(
10164        &r#"
10165        use some::modified;
10166
10167
10168        fn main() {
10169        ˇ    println!("hello there");
10170
10171            println!("around the");
10172            println!("world");
10173        }
10174        "#
10175        .unindent(),
10176    );
10177
10178    cx.update_editor(|editor, cx| {
10179        editor.fold(&Fold, cx);
10180
10181        //Make sure that the fold only gets one hunk
10182        for _ in 0..4 {
10183            editor.go_to_next_hunk(&GoToHunk, cx);
10184        }
10185    });
10186
10187    cx.assert_editor_state(
10188        &r#"
10189        ˇuse some::modified;
10190
10191
10192        fn main() {
10193            println!("hello there");
10194
10195            println!("around the");
10196            println!("world");
10197        }
10198        "#
10199        .unindent(),
10200    );
10201}
10202
10203#[test]
10204fn test_split_words() {
10205    fn split(text: &str) -> Vec<&str> {
10206        split_words(text).collect()
10207    }
10208
10209    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10210    assert_eq!(split("hello_world"), &["hello_", "world"]);
10211    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10212    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10213    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10214    assert_eq!(split("helloworld"), &["helloworld"]);
10215
10216    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10217}
10218
10219#[gpui::test]
10220async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10221    init_test(cx, |_| {});
10222
10223    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10224    let mut assert = |before, after| {
10225        let _state_context = cx.set_state(before);
10226        cx.update_editor(|editor, cx| {
10227            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10228        });
10229        cx.assert_editor_state(after);
10230    };
10231
10232    // Outside bracket jumps to outside of matching bracket
10233    assert("console.logˇ(var);", "console.log(var)ˇ;");
10234    assert("console.log(var)ˇ;", "console.logˇ(var);");
10235
10236    // Inside bracket jumps to inside of matching bracket
10237    assert("console.log(ˇvar);", "console.log(varˇ);");
10238    assert("console.log(varˇ);", "console.log(ˇvar);");
10239
10240    // When outside a bracket and inside, favor jumping to the inside bracket
10241    assert(
10242        "console.log('foo', [1, 2, 3]ˇ);",
10243        "console.log(ˇ'foo', [1, 2, 3]);",
10244    );
10245    assert(
10246        "console.log(ˇ'foo', [1, 2, 3]);",
10247        "console.log('foo', [1, 2, 3]ˇ);",
10248    );
10249
10250    // Bias forward if two options are equally likely
10251    assert(
10252        "let result = curried_fun()ˇ();",
10253        "let result = curried_fun()()ˇ;",
10254    );
10255
10256    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10257    assert(
10258        indoc! {"
10259            function test() {
10260                console.log('test')ˇ
10261            }"},
10262        indoc! {"
10263            function test() {
10264                console.logˇ('test')
10265            }"},
10266    );
10267}
10268
10269#[gpui::test]
10270async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10271    init_test(cx, |_| {});
10272
10273    let fs = FakeFs::new(cx.executor());
10274    fs.insert_tree(
10275        "/a",
10276        json!({
10277            "main.rs": "fn main() { let a = 5; }",
10278            "other.rs": "// Test file",
10279        }),
10280    )
10281    .await;
10282    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10283
10284    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10285    language_registry.add(Arc::new(Language::new(
10286        LanguageConfig {
10287            name: "Rust".into(),
10288            matcher: LanguageMatcher {
10289                path_suffixes: vec!["rs".to_string()],
10290                ..Default::default()
10291            },
10292            brackets: BracketPairConfig {
10293                pairs: vec![BracketPair {
10294                    start: "{".to_string(),
10295                    end: "}".to_string(),
10296                    close: true,
10297                    surround: true,
10298                    newline: true,
10299                }],
10300                disabled_scopes_by_bracket_ix: Vec::new(),
10301            },
10302            ..Default::default()
10303        },
10304        Some(tree_sitter_rust::LANGUAGE.into()),
10305    )));
10306    let mut fake_servers = language_registry.register_fake_lsp(
10307        "Rust",
10308        FakeLspAdapter {
10309            capabilities: lsp::ServerCapabilities {
10310                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10311                    first_trigger_character: "{".to_string(),
10312                    more_trigger_character: None,
10313                }),
10314                ..Default::default()
10315            },
10316            ..Default::default()
10317        },
10318    );
10319
10320    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10321
10322    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10323
10324    let worktree_id = workspace
10325        .update(cx, |workspace, cx| {
10326            workspace.project().update(cx, |project, cx| {
10327                project.worktrees(cx).next().unwrap().read(cx).id()
10328            })
10329        })
10330        .unwrap();
10331
10332    let buffer = project
10333        .update(cx, |project, cx| {
10334            project.open_local_buffer("/a/main.rs", cx)
10335        })
10336        .await
10337        .unwrap();
10338    cx.executor().run_until_parked();
10339    cx.executor().start_waiting();
10340    let fake_server = fake_servers.next().await.unwrap();
10341    let editor_handle = workspace
10342        .update(cx, |workspace, cx| {
10343            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10344        })
10345        .unwrap()
10346        .await
10347        .unwrap()
10348        .downcast::<Editor>()
10349        .unwrap();
10350
10351    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10352        assert_eq!(
10353            params.text_document_position.text_document.uri,
10354            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10355        );
10356        assert_eq!(
10357            params.text_document_position.position,
10358            lsp::Position::new(0, 21),
10359        );
10360
10361        Ok(Some(vec![lsp::TextEdit {
10362            new_text: "]".to_string(),
10363            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10364        }]))
10365    });
10366
10367    editor_handle.update(cx, |editor, cx| {
10368        editor.focus(cx);
10369        editor.change_selections(None, cx, |s| {
10370            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10371        });
10372        editor.handle_input("{", cx);
10373    });
10374
10375    cx.executor().run_until_parked();
10376
10377    buffer.update(cx, |buffer, _| {
10378        assert_eq!(
10379            buffer.text(),
10380            "fn main() { let a = {5}; }",
10381            "No extra braces from on type formatting should appear in the buffer"
10382        )
10383    });
10384}
10385
10386#[gpui::test]
10387async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10388    init_test(cx, |_| {});
10389
10390    let fs = FakeFs::new(cx.executor());
10391    fs.insert_tree(
10392        "/a",
10393        json!({
10394            "main.rs": "fn main() { let a = 5; }",
10395            "other.rs": "// Test file",
10396        }),
10397    )
10398    .await;
10399
10400    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10401
10402    let server_restarts = Arc::new(AtomicUsize::new(0));
10403    let closure_restarts = Arc::clone(&server_restarts);
10404    let language_server_name = "test language server";
10405    let language_name: LanguageName = "Rust".into();
10406
10407    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10408    language_registry.add(Arc::new(Language::new(
10409        LanguageConfig {
10410            name: language_name.clone(),
10411            matcher: LanguageMatcher {
10412                path_suffixes: vec!["rs".to_string()],
10413                ..Default::default()
10414            },
10415            ..Default::default()
10416        },
10417        Some(tree_sitter_rust::LANGUAGE.into()),
10418    )));
10419    let mut fake_servers = language_registry.register_fake_lsp(
10420        "Rust",
10421        FakeLspAdapter {
10422            name: language_server_name,
10423            initialization_options: Some(json!({
10424                "testOptionValue": true
10425            })),
10426            initializer: Some(Box::new(move |fake_server| {
10427                let task_restarts = Arc::clone(&closure_restarts);
10428                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10429                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10430                    futures::future::ready(Ok(()))
10431                });
10432            })),
10433            ..Default::default()
10434        },
10435    );
10436
10437    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10438    let _buffer = project
10439        .update(cx, |project, cx| {
10440            project.open_local_buffer("/a/main.rs", cx)
10441        })
10442        .await
10443        .unwrap();
10444    let _fake_server = fake_servers.next().await.unwrap();
10445    update_test_language_settings(cx, |language_settings| {
10446        language_settings.languages.insert(
10447            language_name.clone(),
10448            LanguageSettingsContent {
10449                tab_size: NonZeroU32::new(8),
10450                ..Default::default()
10451            },
10452        );
10453    });
10454    cx.executor().run_until_parked();
10455    assert_eq!(
10456        server_restarts.load(atomic::Ordering::Acquire),
10457        0,
10458        "Should not restart LSP server on an unrelated change"
10459    );
10460
10461    update_test_project_settings(cx, |project_settings| {
10462        project_settings.lsp.insert(
10463            "Some other server name".into(),
10464            LspSettings {
10465                binary: None,
10466                settings: None,
10467                initialization_options: Some(json!({
10468                    "some other init value": false
10469                })),
10470            },
10471        );
10472    });
10473    cx.executor().run_until_parked();
10474    assert_eq!(
10475        server_restarts.load(atomic::Ordering::Acquire),
10476        0,
10477        "Should not restart LSP server on an unrelated LSP settings change"
10478    );
10479
10480    update_test_project_settings(cx, |project_settings| {
10481        project_settings.lsp.insert(
10482            language_server_name.into(),
10483            LspSettings {
10484                binary: None,
10485                settings: None,
10486                initialization_options: Some(json!({
10487                    "anotherInitValue": false
10488                })),
10489            },
10490        );
10491    });
10492    cx.executor().run_until_parked();
10493    assert_eq!(
10494        server_restarts.load(atomic::Ordering::Acquire),
10495        1,
10496        "Should restart LSP server on a related LSP settings change"
10497    );
10498
10499    update_test_project_settings(cx, |project_settings| {
10500        project_settings.lsp.insert(
10501            language_server_name.into(),
10502            LspSettings {
10503                binary: None,
10504                settings: None,
10505                initialization_options: Some(json!({
10506                    "anotherInitValue": false
10507                })),
10508            },
10509        );
10510    });
10511    cx.executor().run_until_parked();
10512    assert_eq!(
10513        server_restarts.load(atomic::Ordering::Acquire),
10514        1,
10515        "Should not restart LSP server on a related LSP settings change that is the same"
10516    );
10517
10518    update_test_project_settings(cx, |project_settings| {
10519        project_settings.lsp.insert(
10520            language_server_name.into(),
10521            LspSettings {
10522                binary: None,
10523                settings: None,
10524                initialization_options: None,
10525            },
10526        );
10527    });
10528    cx.executor().run_until_parked();
10529    assert_eq!(
10530        server_restarts.load(atomic::Ordering::Acquire),
10531        2,
10532        "Should restart LSP server on another related LSP settings change"
10533    );
10534}
10535
10536#[gpui::test]
10537async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10538    init_test(cx, |_| {});
10539
10540    let mut cx = EditorLspTestContext::new_rust(
10541        lsp::ServerCapabilities {
10542            completion_provider: Some(lsp::CompletionOptions {
10543                trigger_characters: Some(vec![".".to_string()]),
10544                resolve_provider: Some(true),
10545                ..Default::default()
10546            }),
10547            ..Default::default()
10548        },
10549        cx,
10550    )
10551    .await;
10552
10553    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10554    cx.simulate_keystroke(".");
10555    let completion_item = lsp::CompletionItem {
10556        label: "some".into(),
10557        kind: Some(lsp::CompletionItemKind::SNIPPET),
10558        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10559        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10560            kind: lsp::MarkupKind::Markdown,
10561            value: "```rust\nSome(2)\n```".to_string(),
10562        })),
10563        deprecated: Some(false),
10564        sort_text: Some("fffffff2".to_string()),
10565        filter_text: Some("some".to_string()),
10566        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10567        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10568            range: lsp::Range {
10569                start: lsp::Position {
10570                    line: 0,
10571                    character: 22,
10572                },
10573                end: lsp::Position {
10574                    line: 0,
10575                    character: 22,
10576                },
10577            },
10578            new_text: "Some(2)".to_string(),
10579        })),
10580        additional_text_edits: Some(vec![lsp::TextEdit {
10581            range: lsp::Range {
10582                start: lsp::Position {
10583                    line: 0,
10584                    character: 20,
10585                },
10586                end: lsp::Position {
10587                    line: 0,
10588                    character: 22,
10589                },
10590            },
10591            new_text: "".to_string(),
10592        }]),
10593        ..Default::default()
10594    };
10595
10596    let closure_completion_item = completion_item.clone();
10597    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10598        let task_completion_item = closure_completion_item.clone();
10599        async move {
10600            Ok(Some(lsp::CompletionResponse::Array(vec![
10601                task_completion_item,
10602            ])))
10603        }
10604    });
10605
10606    request.next().await;
10607
10608    cx.condition(|editor, _| editor.context_menu_visible())
10609        .await;
10610    let apply_additional_edits = cx.update_editor(|editor, cx| {
10611        editor
10612            .confirm_completion(&ConfirmCompletion::default(), cx)
10613            .unwrap()
10614    });
10615    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10616
10617    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10618        let task_completion_item = completion_item.clone();
10619        async move { Ok(task_completion_item) }
10620    })
10621    .next()
10622    .await
10623    .unwrap();
10624    apply_additional_edits.await.unwrap();
10625    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10626}
10627
10628#[gpui::test]
10629async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10630    init_test(cx, |_| {});
10631
10632    let mut cx = EditorLspTestContext::new_rust(
10633        lsp::ServerCapabilities {
10634            completion_provider: Some(lsp::CompletionOptions {
10635                trigger_characters: Some(vec![".".to_string()]),
10636                resolve_provider: Some(true),
10637                ..Default::default()
10638            }),
10639            ..Default::default()
10640        },
10641        cx,
10642    )
10643    .await;
10644
10645    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10646    cx.simulate_keystroke(".");
10647
10648    let default_commit_characters = vec!["?".to_string()];
10649    let default_data = json!({ "very": "special"});
10650    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10651    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10652    let default_edit_range = lsp::Range {
10653        start: lsp::Position {
10654            line: 0,
10655            character: 5,
10656        },
10657        end: lsp::Position {
10658            line: 0,
10659            character: 5,
10660        },
10661    };
10662
10663    let completion_data = default_data.clone();
10664    let completion_characters = default_commit_characters.clone();
10665    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10666        let default_data = completion_data.clone();
10667        let default_commit_characters = completion_characters.clone();
10668        async move {
10669            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10670                items: vec![
10671                    lsp::CompletionItem {
10672                        label: "Some(2)".into(),
10673                        insert_text: Some("Some(2)".into()),
10674                        data: Some(json!({ "very": "special"})),
10675                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10676                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10677                            lsp::InsertReplaceEdit {
10678                                new_text: "Some(2)".to_string(),
10679                                insert: lsp::Range::default(),
10680                                replace: lsp::Range::default(),
10681                            },
10682                        )),
10683                        ..lsp::CompletionItem::default()
10684                    },
10685                    lsp::CompletionItem {
10686                        label: "vec![2]".into(),
10687                        insert_text: Some("vec![2]".into()),
10688                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10689                        ..lsp::CompletionItem::default()
10690                    },
10691                ],
10692                item_defaults: Some(lsp::CompletionListItemDefaults {
10693                    data: Some(default_data.clone()),
10694                    commit_characters: Some(default_commit_characters.clone()),
10695                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10696                        default_edit_range,
10697                    )),
10698                    insert_text_format: Some(default_insert_text_format),
10699                    insert_text_mode: Some(default_insert_text_mode),
10700                }),
10701                ..lsp::CompletionList::default()
10702            })))
10703        }
10704    })
10705    .next()
10706    .await;
10707
10708    cx.condition(|editor, _| editor.context_menu_visible())
10709        .await;
10710
10711    cx.update_editor(|editor, _| {
10712        let menu = editor.context_menu.read();
10713        match menu.as_ref().expect("should have the completions menu") {
10714            ContextMenu::Completions(completions_menu) => {
10715                assert_eq!(
10716                    completions_menu
10717                        .matches
10718                        .iter()
10719                        .map(|c| c.string.as_str())
10720                        .collect::<Vec<_>>(),
10721                    vec!["Some(2)", "vec![2]"]
10722                );
10723            }
10724            ContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10725        }
10726    });
10727
10728    cx.update_editor(|editor, cx| {
10729        editor.context_menu_first(&ContextMenuFirst, cx);
10730    });
10731    let first_item_resolve_characters = default_commit_characters.clone();
10732    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, item_to_resolve, _| {
10733        let default_commit_characters = first_item_resolve_characters.clone();
10734
10735        async move {
10736            assert_eq!(
10737                item_to_resolve.label, "Some(2)",
10738                "Should have selected the first item"
10739            );
10740            assert_eq!(
10741                item_to_resolve.data,
10742                Some(json!({ "very": "special"})),
10743                "First item should bring its own data for resolving"
10744            );
10745            assert_eq!(
10746                item_to_resolve.commit_characters,
10747                Some(default_commit_characters),
10748                "First item had no own commit characters and should inherit the default ones"
10749            );
10750            assert!(
10751                matches!(
10752                    item_to_resolve.text_edit,
10753                    Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10754                ),
10755                "First item should bring its own edit range for resolving"
10756            );
10757            assert_eq!(
10758                item_to_resolve.insert_text_format,
10759                Some(default_insert_text_format),
10760                "First item had no own insert text format and should inherit the default one"
10761            );
10762            assert_eq!(
10763                item_to_resolve.insert_text_mode,
10764                Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10765                "First item should bring its own insert text mode for resolving"
10766            );
10767            Ok(item_to_resolve)
10768        }
10769    })
10770    .next()
10771    .await
10772    .unwrap();
10773
10774    cx.update_editor(|editor, cx| {
10775        editor.context_menu_last(&ContextMenuLast, cx);
10776    });
10777    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, item_to_resolve, _| {
10778        let default_data = default_data.clone();
10779        let default_commit_characters = default_commit_characters.clone();
10780        async move {
10781            assert_eq!(
10782                item_to_resolve.label, "vec![2]",
10783                "Should have selected the last item"
10784            );
10785            assert_eq!(
10786                item_to_resolve.data,
10787                Some(default_data),
10788                "Last item has no own resolve data and should inherit the default one"
10789            );
10790            assert_eq!(
10791                item_to_resolve.commit_characters,
10792                Some(default_commit_characters),
10793                "Last item had no own commit characters and should inherit the default ones"
10794            );
10795            assert_eq!(
10796                item_to_resolve.text_edit,
10797                Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10798                    range: default_edit_range,
10799                    new_text: "vec![2]".to_string()
10800                })),
10801                "Last item had no own edit range and should inherit the default one"
10802            );
10803            assert_eq!(
10804                item_to_resolve.insert_text_format,
10805                Some(lsp::InsertTextFormat::PLAIN_TEXT),
10806                "Last item should bring its own insert text format for resolving"
10807            );
10808            assert_eq!(
10809                item_to_resolve.insert_text_mode,
10810                Some(default_insert_text_mode),
10811                "Last item had no own insert text mode and should inherit the default one"
10812            );
10813
10814            Ok(item_to_resolve)
10815        }
10816    })
10817    .next()
10818    .await
10819    .unwrap();
10820}
10821
10822#[gpui::test]
10823async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10824    init_test(cx, |_| {});
10825
10826    let mut cx = EditorLspTestContext::new(
10827        Language::new(
10828            LanguageConfig {
10829                matcher: LanguageMatcher {
10830                    path_suffixes: vec!["jsx".into()],
10831                    ..Default::default()
10832                },
10833                overrides: [(
10834                    "element".into(),
10835                    LanguageConfigOverride {
10836                        word_characters: Override::Set(['-'].into_iter().collect()),
10837                        ..Default::default()
10838                    },
10839                )]
10840                .into_iter()
10841                .collect(),
10842                ..Default::default()
10843            },
10844            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10845        )
10846        .with_override_query("(jsx_self_closing_element) @element")
10847        .unwrap(),
10848        lsp::ServerCapabilities {
10849            completion_provider: Some(lsp::CompletionOptions {
10850                trigger_characters: Some(vec![":".to_string()]),
10851                ..Default::default()
10852            }),
10853            ..Default::default()
10854        },
10855        cx,
10856    )
10857    .await;
10858
10859    cx.lsp
10860        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10861            Ok(Some(lsp::CompletionResponse::Array(vec![
10862                lsp::CompletionItem {
10863                    label: "bg-blue".into(),
10864                    ..Default::default()
10865                },
10866                lsp::CompletionItem {
10867                    label: "bg-red".into(),
10868                    ..Default::default()
10869                },
10870                lsp::CompletionItem {
10871                    label: "bg-yellow".into(),
10872                    ..Default::default()
10873                },
10874            ])))
10875        });
10876
10877    cx.set_state(r#"<p class="bgˇ" />"#);
10878
10879    // Trigger completion when typing a dash, because the dash is an extra
10880    // word character in the 'element' scope, which contains the cursor.
10881    cx.simulate_keystroke("-");
10882    cx.executor().run_until_parked();
10883    cx.update_editor(|editor, _| {
10884        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10885            assert_eq!(
10886                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10887                &["bg-red", "bg-blue", "bg-yellow"]
10888            );
10889        } else {
10890            panic!("expected completion menu to be open");
10891        }
10892    });
10893
10894    cx.simulate_keystroke("l");
10895    cx.executor().run_until_parked();
10896    cx.update_editor(|editor, _| {
10897        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10898            assert_eq!(
10899                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10900                &["bg-blue", "bg-yellow"]
10901            );
10902        } else {
10903            panic!("expected completion menu to be open");
10904        }
10905    });
10906
10907    // When filtering completions, consider the character after the '-' to
10908    // be the start of a subword.
10909    cx.set_state(r#"<p class="yelˇ" />"#);
10910    cx.simulate_keystroke("l");
10911    cx.executor().run_until_parked();
10912    cx.update_editor(|editor, _| {
10913        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10914            assert_eq!(
10915                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10916                &["bg-yellow"]
10917            );
10918        } else {
10919            panic!("expected completion menu to be open");
10920        }
10921    });
10922}
10923
10924#[gpui::test]
10925async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10926    init_test(cx, |settings| {
10927        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10928            FormatterList(vec![Formatter::Prettier].into()),
10929        ))
10930    });
10931
10932    let fs = FakeFs::new(cx.executor());
10933    fs.insert_file("/file.ts", Default::default()).await;
10934
10935    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10936    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10937
10938    language_registry.add(Arc::new(Language::new(
10939        LanguageConfig {
10940            name: "TypeScript".into(),
10941            matcher: LanguageMatcher {
10942                path_suffixes: vec!["ts".to_string()],
10943                ..Default::default()
10944            },
10945            ..Default::default()
10946        },
10947        Some(tree_sitter_rust::LANGUAGE.into()),
10948    )));
10949    update_test_language_settings(cx, |settings| {
10950        settings.defaults.prettier = Some(PrettierSettings {
10951            allowed: true,
10952            ..PrettierSettings::default()
10953        });
10954    });
10955
10956    let test_plugin = "test_plugin";
10957    let _ = language_registry.register_fake_lsp(
10958        "TypeScript",
10959        FakeLspAdapter {
10960            prettier_plugins: vec![test_plugin],
10961            ..Default::default()
10962        },
10963    );
10964
10965    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10966    let buffer = project
10967        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10968        .await
10969        .unwrap();
10970
10971    let buffer_text = "one\ntwo\nthree\n";
10972    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10973    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10974    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10975
10976    editor
10977        .update(cx, |editor, cx| {
10978            editor.perform_format(
10979                project.clone(),
10980                FormatTrigger::Manual,
10981                FormatTarget::Buffer,
10982                cx,
10983            )
10984        })
10985        .unwrap()
10986        .await;
10987    assert_eq!(
10988        editor.update(cx, |editor, cx| editor.text(cx)),
10989        buffer_text.to_string() + prettier_format_suffix,
10990        "Test prettier formatting was not applied to the original buffer text",
10991    );
10992
10993    update_test_language_settings(cx, |settings| {
10994        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10995    });
10996    let format = editor.update(cx, |editor, cx| {
10997        editor.perform_format(
10998            project.clone(),
10999            FormatTrigger::Manual,
11000            FormatTarget::Buffer,
11001            cx,
11002        )
11003    });
11004    format.await.unwrap();
11005    assert_eq!(
11006        editor.update(cx, |editor, cx| editor.text(cx)),
11007        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11008        "Autoformatting (via test prettier) was not applied to the original buffer text",
11009    );
11010}
11011
11012#[gpui::test]
11013async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11014    init_test(cx, |_| {});
11015    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11016    let base_text = indoc! {r#"struct Row;
11017struct Row1;
11018struct Row2;
11019
11020struct Row4;
11021struct Row5;
11022struct Row6;
11023
11024struct Row8;
11025struct Row9;
11026struct Row10;"#};
11027
11028    // When addition hunks are not adjacent to carets, no hunk revert is performed
11029    assert_hunk_revert(
11030        indoc! {r#"struct Row;
11031                   struct Row1;
11032                   struct Row1.1;
11033                   struct Row1.2;
11034                   struct Row2;ˇ
11035
11036                   struct Row4;
11037                   struct Row5;
11038                   struct Row6;
11039
11040                   struct Row8;
11041                   ˇstruct Row9;
11042                   struct Row9.1;
11043                   struct Row9.2;
11044                   struct Row9.3;
11045                   struct Row10;"#},
11046        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11047        indoc! {r#"struct Row;
11048                   struct Row1;
11049                   struct Row1.1;
11050                   struct Row1.2;
11051                   struct Row2;ˇ
11052
11053                   struct Row4;
11054                   struct Row5;
11055                   struct Row6;
11056
11057                   struct Row8;
11058                   ˇstruct Row9;
11059                   struct Row9.1;
11060                   struct Row9.2;
11061                   struct Row9.3;
11062                   struct Row10;"#},
11063        base_text,
11064        &mut cx,
11065    );
11066    // Same for selections
11067    assert_hunk_revert(
11068        indoc! {r#"struct Row;
11069                   struct Row1;
11070                   struct Row2;
11071                   struct Row2.1;
11072                   struct Row2.2;
11073                   «ˇ
11074                   struct Row4;
11075                   struct» Row5;
11076                   «struct Row6;
11077                   ˇ»
11078                   struct Row9.1;
11079                   struct Row9.2;
11080                   struct Row9.3;
11081                   struct Row8;
11082                   struct Row9;
11083                   struct Row10;"#},
11084        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11085        indoc! {r#"struct Row;
11086                   struct Row1;
11087                   struct Row2;
11088                   struct Row2.1;
11089                   struct Row2.2;
11090                   «ˇ
11091                   struct Row4;
11092                   struct» Row5;
11093                   «struct Row6;
11094                   ˇ»
11095                   struct Row9.1;
11096                   struct Row9.2;
11097                   struct Row9.3;
11098                   struct Row8;
11099                   struct Row9;
11100                   struct Row10;"#},
11101        base_text,
11102        &mut cx,
11103    );
11104
11105    // When carets and selections intersect the addition hunks, those are reverted.
11106    // Adjacent carets got merged.
11107    assert_hunk_revert(
11108        indoc! {r#"struct Row;
11109                   ˇ// something on the top
11110                   struct Row1;
11111                   struct Row2;
11112                   struct Roˇw3.1;
11113                   struct Row2.2;
11114                   struct Row2.3;ˇ
11115
11116                   struct Row4;
11117                   struct ˇRow5.1;
11118                   struct Row5.2;
11119                   struct «Rowˇ»5.3;
11120                   struct Row5;
11121                   struct Row6;
11122                   ˇ
11123                   struct Row9.1;
11124                   struct «Rowˇ»9.2;
11125                   struct «ˇRow»9.3;
11126                   struct Row8;
11127                   struct Row9;
11128                   «ˇ// something on bottom»
11129                   struct Row10;"#},
11130        vec![
11131            DiffHunkStatus::Added,
11132            DiffHunkStatus::Added,
11133            DiffHunkStatus::Added,
11134            DiffHunkStatus::Added,
11135            DiffHunkStatus::Added,
11136        ],
11137        indoc! {r#"struct Row;
11138                   ˇstruct Row1;
11139                   struct Row2;
11140                   ˇ
11141                   struct Row4;
11142                   ˇstruct Row5;
11143                   struct Row6;
11144                   ˇ
11145                   ˇstruct Row8;
11146                   struct Row9;
11147                   ˇstruct Row10;"#},
11148        base_text,
11149        &mut cx,
11150    );
11151}
11152
11153#[gpui::test]
11154async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11155    init_test(cx, |_| {});
11156    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11157    let base_text = indoc! {r#"struct Row;
11158struct Row1;
11159struct Row2;
11160
11161struct Row4;
11162struct Row5;
11163struct Row6;
11164
11165struct Row8;
11166struct Row9;
11167struct Row10;"#};
11168
11169    // Modification hunks behave the same as the addition ones.
11170    assert_hunk_revert(
11171        indoc! {r#"struct Row;
11172                   struct Row1;
11173                   struct Row33;
11174                   ˇ
11175                   struct Row4;
11176                   struct Row5;
11177                   struct Row6;
11178                   ˇ
11179                   struct Row99;
11180                   struct Row9;
11181                   struct Row10;"#},
11182        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11183        indoc! {r#"struct Row;
11184                   struct Row1;
11185                   struct Row33;
11186                   ˇ
11187                   struct Row4;
11188                   struct Row5;
11189                   struct Row6;
11190                   ˇ
11191                   struct Row99;
11192                   struct Row9;
11193                   struct Row10;"#},
11194        base_text,
11195        &mut cx,
11196    );
11197    assert_hunk_revert(
11198        indoc! {r#"struct Row;
11199                   struct Row1;
11200                   struct Row33;
11201                   «ˇ
11202                   struct Row4;
11203                   struct» Row5;
11204                   «struct Row6;
11205                   ˇ»
11206                   struct Row99;
11207                   struct Row9;
11208                   struct Row10;"#},
11209        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11210        indoc! {r#"struct Row;
11211                   struct Row1;
11212                   struct Row33;
11213                   «ˇ
11214                   struct Row4;
11215                   struct» Row5;
11216                   «struct Row6;
11217                   ˇ»
11218                   struct Row99;
11219                   struct Row9;
11220                   struct Row10;"#},
11221        base_text,
11222        &mut cx,
11223    );
11224
11225    assert_hunk_revert(
11226        indoc! {r#"ˇstruct Row1.1;
11227                   struct Row1;
11228                   «ˇstr»uct Row22;
11229
11230                   struct ˇRow44;
11231                   struct Row5;
11232                   struct «Rˇ»ow66;ˇ
11233
11234                   «struˇ»ct Row88;
11235                   struct Row9;
11236                   struct Row1011;ˇ"#},
11237        vec![
11238            DiffHunkStatus::Modified,
11239            DiffHunkStatus::Modified,
11240            DiffHunkStatus::Modified,
11241            DiffHunkStatus::Modified,
11242            DiffHunkStatus::Modified,
11243            DiffHunkStatus::Modified,
11244        ],
11245        indoc! {r#"struct Row;
11246                   ˇstruct Row1;
11247                   struct Row2;
11248                   ˇ
11249                   struct Row4;
11250                   ˇstruct Row5;
11251                   struct Row6;
11252                   ˇ
11253                   struct Row8;
11254                   ˇstruct Row9;
11255                   struct Row10;ˇ"#},
11256        base_text,
11257        &mut cx,
11258    );
11259}
11260
11261#[gpui::test]
11262async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11263    init_test(cx, |_| {});
11264    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11265    let base_text = indoc! {r#"struct Row;
11266struct Row1;
11267struct Row2;
11268
11269struct Row4;
11270struct Row5;
11271struct Row6;
11272
11273struct Row8;
11274struct Row9;
11275struct Row10;"#};
11276
11277    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11278    assert_hunk_revert(
11279        indoc! {r#"struct Row;
11280                   struct Row2;
11281
11282                   ˇstruct Row4;
11283                   struct Row5;
11284                   struct Row6;
11285                   ˇ
11286                   struct Row8;
11287                   struct Row10;"#},
11288        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11289        indoc! {r#"struct Row;
11290                   struct Row2;
11291
11292                   ˇstruct Row4;
11293                   struct Row5;
11294                   struct Row6;
11295                   ˇ
11296                   struct Row8;
11297                   struct Row10;"#},
11298        base_text,
11299        &mut cx,
11300    );
11301    assert_hunk_revert(
11302        indoc! {r#"struct Row;
11303                   struct Row2;
11304
11305                   «ˇstruct Row4;
11306                   struct» Row5;
11307                   «struct Row6;
11308                   ˇ»
11309                   struct Row8;
11310                   struct Row10;"#},
11311        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11312        indoc! {r#"struct Row;
11313                   struct Row2;
11314
11315                   «ˇstruct Row4;
11316                   struct» Row5;
11317                   «struct Row6;
11318                   ˇ»
11319                   struct Row8;
11320                   struct Row10;"#},
11321        base_text,
11322        &mut cx,
11323    );
11324
11325    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11326    assert_hunk_revert(
11327        indoc! {r#"struct Row;
11328                   ˇstruct Row2;
11329
11330                   struct Row4;
11331                   struct Row5;
11332                   struct Row6;
11333
11334                   struct Row8;ˇ
11335                   struct Row10;"#},
11336        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11337        indoc! {r#"struct Row;
11338                   struct Row1;
11339                   ˇstruct Row2;
11340
11341                   struct Row4;
11342                   struct Row5;
11343                   struct Row6;
11344
11345                   struct Row8;ˇ
11346                   struct Row9;
11347                   struct Row10;"#},
11348        base_text,
11349        &mut cx,
11350    );
11351    assert_hunk_revert(
11352        indoc! {r#"struct Row;
11353                   struct Row2«ˇ;
11354                   struct Row4;
11355                   struct» Row5;
11356                   «struct Row6;
11357
11358                   struct Row8;ˇ»
11359                   struct Row10;"#},
11360        vec![
11361            DiffHunkStatus::Removed,
11362            DiffHunkStatus::Removed,
11363            DiffHunkStatus::Removed,
11364        ],
11365        indoc! {r#"struct Row;
11366                   struct Row1;
11367                   struct Row2«ˇ;
11368
11369                   struct Row4;
11370                   struct» Row5;
11371                   «struct Row6;
11372
11373                   struct Row8;ˇ»
11374                   struct Row9;
11375                   struct Row10;"#},
11376        base_text,
11377        &mut cx,
11378    );
11379}
11380
11381#[gpui::test]
11382async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11383    init_test(cx, |_| {});
11384
11385    let cols = 4;
11386    let rows = 10;
11387    let sample_text_1 = sample_text(rows, cols, 'a');
11388    assert_eq!(
11389        sample_text_1,
11390        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11391    );
11392    let sample_text_2 = sample_text(rows, cols, 'l');
11393    assert_eq!(
11394        sample_text_2,
11395        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11396    );
11397    let sample_text_3 = sample_text(rows, cols, 'v');
11398    assert_eq!(
11399        sample_text_3,
11400        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11401    );
11402
11403    fn diff_every_buffer_row(
11404        buffer: &Model<Buffer>,
11405        sample_text: String,
11406        cols: usize,
11407        cx: &mut gpui::TestAppContext,
11408    ) {
11409        // revert first character in each row, creating one large diff hunk per buffer
11410        let is_first_char = |offset: usize| offset % cols == 0;
11411        buffer.update(cx, |buffer, cx| {
11412            buffer.set_text(
11413                sample_text
11414                    .chars()
11415                    .enumerate()
11416                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11417                    .collect::<String>(),
11418                cx,
11419            );
11420            buffer.set_diff_base(Some(sample_text), cx);
11421        });
11422        cx.executor().run_until_parked();
11423    }
11424
11425    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11426    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11427
11428    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11429    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11430
11431    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11432    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11433
11434    let multibuffer = cx.new_model(|cx| {
11435        let mut multibuffer = MultiBuffer::new(ReadWrite);
11436        multibuffer.push_excerpts(
11437            buffer_1.clone(),
11438            [
11439                ExcerptRange {
11440                    context: Point::new(0, 0)..Point::new(3, 0),
11441                    primary: None,
11442                },
11443                ExcerptRange {
11444                    context: Point::new(5, 0)..Point::new(7, 0),
11445                    primary: None,
11446                },
11447                ExcerptRange {
11448                    context: Point::new(9, 0)..Point::new(10, 4),
11449                    primary: None,
11450                },
11451            ],
11452            cx,
11453        );
11454        multibuffer.push_excerpts(
11455            buffer_2.clone(),
11456            [
11457                ExcerptRange {
11458                    context: Point::new(0, 0)..Point::new(3, 0),
11459                    primary: None,
11460                },
11461                ExcerptRange {
11462                    context: Point::new(5, 0)..Point::new(7, 0),
11463                    primary: None,
11464                },
11465                ExcerptRange {
11466                    context: Point::new(9, 0)..Point::new(10, 4),
11467                    primary: None,
11468                },
11469            ],
11470            cx,
11471        );
11472        multibuffer.push_excerpts(
11473            buffer_3.clone(),
11474            [
11475                ExcerptRange {
11476                    context: Point::new(0, 0)..Point::new(3, 0),
11477                    primary: None,
11478                },
11479                ExcerptRange {
11480                    context: Point::new(5, 0)..Point::new(7, 0),
11481                    primary: None,
11482                },
11483                ExcerptRange {
11484                    context: Point::new(9, 0)..Point::new(10, 4),
11485                    primary: None,
11486                },
11487            ],
11488            cx,
11489        );
11490        multibuffer
11491    });
11492
11493    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11494    editor.update(cx, |editor, cx| {
11495        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
11496        editor.select_all(&SelectAll, cx);
11497        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11498    });
11499    cx.executor().run_until_parked();
11500    // When all ranges are selected, all buffer hunks are reverted.
11501    editor.update(cx, |editor, cx| {
11502        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");
11503    });
11504    buffer_1.update(cx, |buffer, _| {
11505        assert_eq!(buffer.text(), sample_text_1);
11506    });
11507    buffer_2.update(cx, |buffer, _| {
11508        assert_eq!(buffer.text(), sample_text_2);
11509    });
11510    buffer_3.update(cx, |buffer, _| {
11511        assert_eq!(buffer.text(), sample_text_3);
11512    });
11513
11514    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11515    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11516    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11517    editor.update(cx, |editor, cx| {
11518        editor.change_selections(None, cx, |s| {
11519            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11520        });
11521        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11522    });
11523    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11524    // but not affect buffer_2 and its related excerpts.
11525    editor.update(cx, |editor, cx| {
11526        assert_eq!(
11527            editor.text(cx),
11528            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
11529        );
11530    });
11531    buffer_1.update(cx, |buffer, _| {
11532        assert_eq!(buffer.text(), sample_text_1);
11533    });
11534    buffer_2.update(cx, |buffer, _| {
11535        assert_eq!(
11536            buffer.text(),
11537            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11538        );
11539    });
11540    buffer_3.update(cx, |buffer, _| {
11541        assert_eq!(
11542            buffer.text(),
11543            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11544        );
11545    });
11546}
11547
11548#[gpui::test]
11549async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11550    init_test(cx, |_| {});
11551
11552    let cols = 4;
11553    let rows = 10;
11554    let sample_text_1 = sample_text(rows, cols, 'a');
11555    assert_eq!(
11556        sample_text_1,
11557        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11558    );
11559    let sample_text_2 = sample_text(rows, cols, 'l');
11560    assert_eq!(
11561        sample_text_2,
11562        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11563    );
11564    let sample_text_3 = sample_text(rows, cols, 'v');
11565    assert_eq!(
11566        sample_text_3,
11567        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11568    );
11569
11570    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11571    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11572    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11573
11574    let multi_buffer = cx.new_model(|cx| {
11575        let mut multibuffer = MultiBuffer::new(ReadWrite);
11576        multibuffer.push_excerpts(
11577            buffer_1.clone(),
11578            [
11579                ExcerptRange {
11580                    context: Point::new(0, 0)..Point::new(3, 0),
11581                    primary: None,
11582                },
11583                ExcerptRange {
11584                    context: Point::new(5, 0)..Point::new(7, 0),
11585                    primary: None,
11586                },
11587                ExcerptRange {
11588                    context: Point::new(9, 0)..Point::new(10, 4),
11589                    primary: None,
11590                },
11591            ],
11592            cx,
11593        );
11594        multibuffer.push_excerpts(
11595            buffer_2.clone(),
11596            [
11597                ExcerptRange {
11598                    context: Point::new(0, 0)..Point::new(3, 0),
11599                    primary: None,
11600                },
11601                ExcerptRange {
11602                    context: Point::new(5, 0)..Point::new(7, 0),
11603                    primary: None,
11604                },
11605                ExcerptRange {
11606                    context: Point::new(9, 0)..Point::new(10, 4),
11607                    primary: None,
11608                },
11609            ],
11610            cx,
11611        );
11612        multibuffer.push_excerpts(
11613            buffer_3.clone(),
11614            [
11615                ExcerptRange {
11616                    context: Point::new(0, 0)..Point::new(3, 0),
11617                    primary: None,
11618                },
11619                ExcerptRange {
11620                    context: Point::new(5, 0)..Point::new(7, 0),
11621                    primary: None,
11622                },
11623                ExcerptRange {
11624                    context: Point::new(9, 0)..Point::new(10, 4),
11625                    primary: None,
11626                },
11627            ],
11628            cx,
11629        );
11630        multibuffer
11631    });
11632
11633    let fs = FakeFs::new(cx.executor());
11634    fs.insert_tree(
11635        "/a",
11636        json!({
11637            "main.rs": sample_text_1,
11638            "other.rs": sample_text_2,
11639            "lib.rs": sample_text_3,
11640        }),
11641    )
11642    .await;
11643    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11644    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11645    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11646    let multi_buffer_editor = cx.new_view(|cx| {
11647        Editor::new(
11648            EditorMode::Full,
11649            multi_buffer,
11650            Some(project.clone()),
11651            true,
11652            cx,
11653        )
11654    });
11655    let multibuffer_item_id = workspace
11656        .update(cx, |workspace, cx| {
11657            assert!(
11658                workspace.active_item(cx).is_none(),
11659                "active item should be None before the first item is added"
11660            );
11661            workspace.add_item_to_active_pane(
11662                Box::new(multi_buffer_editor.clone()),
11663                None,
11664                true,
11665                cx,
11666            );
11667            let active_item = workspace
11668                .active_item(cx)
11669                .expect("should have an active item after adding the multi buffer");
11670            assert!(
11671                !active_item.is_singleton(cx),
11672                "A multi buffer was expected to active after adding"
11673            );
11674            active_item.item_id()
11675        })
11676        .unwrap();
11677    cx.executor().run_until_parked();
11678
11679    multi_buffer_editor.update(cx, |editor, cx| {
11680        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11681        editor.open_excerpts(&OpenExcerpts, cx);
11682    });
11683    cx.executor().run_until_parked();
11684    let first_item_id = workspace
11685        .update(cx, |workspace, cx| {
11686            let active_item = workspace
11687                .active_item(cx)
11688                .expect("should have an active item after navigating into the 1st buffer");
11689            let first_item_id = active_item.item_id();
11690            assert_ne!(
11691                first_item_id, multibuffer_item_id,
11692                "Should navigate into the 1st buffer and activate it"
11693            );
11694            assert!(
11695                active_item.is_singleton(cx),
11696                "New active item should be a singleton buffer"
11697            );
11698            assert_eq!(
11699                active_item
11700                    .act_as::<Editor>(cx)
11701                    .expect("should have navigated into an editor for the 1st buffer")
11702                    .read(cx)
11703                    .text(cx),
11704                sample_text_1
11705            );
11706
11707            workspace
11708                .go_back(workspace.active_pane().downgrade(), cx)
11709                .detach_and_log_err(cx);
11710
11711            first_item_id
11712        })
11713        .unwrap();
11714    cx.executor().run_until_parked();
11715    workspace
11716        .update(cx, |workspace, cx| {
11717            let active_item = workspace
11718                .active_item(cx)
11719                .expect("should have an active item after navigating back");
11720            assert_eq!(
11721                active_item.item_id(),
11722                multibuffer_item_id,
11723                "Should navigate back to the multi buffer"
11724            );
11725            assert!(!active_item.is_singleton(cx));
11726        })
11727        .unwrap();
11728
11729    multi_buffer_editor.update(cx, |editor, cx| {
11730        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11731            s.select_ranges(Some(39..40))
11732        });
11733        editor.open_excerpts(&OpenExcerpts, cx);
11734    });
11735    cx.executor().run_until_parked();
11736    let second_item_id = workspace
11737        .update(cx, |workspace, cx| {
11738            let active_item = workspace
11739                .active_item(cx)
11740                .expect("should have an active item after navigating into the 2nd buffer");
11741            let second_item_id = active_item.item_id();
11742            assert_ne!(
11743                second_item_id, multibuffer_item_id,
11744                "Should navigate away from the multibuffer"
11745            );
11746            assert_ne!(
11747                second_item_id, first_item_id,
11748                "Should navigate into the 2nd buffer and activate it"
11749            );
11750            assert!(
11751                active_item.is_singleton(cx),
11752                "New active item should be a singleton buffer"
11753            );
11754            assert_eq!(
11755                active_item
11756                    .act_as::<Editor>(cx)
11757                    .expect("should have navigated into an editor")
11758                    .read(cx)
11759                    .text(cx),
11760                sample_text_2
11761            );
11762
11763            workspace
11764                .go_back(workspace.active_pane().downgrade(), cx)
11765                .detach_and_log_err(cx);
11766
11767            second_item_id
11768        })
11769        .unwrap();
11770    cx.executor().run_until_parked();
11771    workspace
11772        .update(cx, |workspace, cx| {
11773            let active_item = workspace
11774                .active_item(cx)
11775                .expect("should have an active item after navigating back from the 2nd buffer");
11776            assert_eq!(
11777                active_item.item_id(),
11778                multibuffer_item_id,
11779                "Should navigate back from the 2nd buffer to the multi buffer"
11780            );
11781            assert!(!active_item.is_singleton(cx));
11782        })
11783        .unwrap();
11784
11785    multi_buffer_editor.update(cx, |editor, cx| {
11786        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11787            s.select_ranges(Some(70..70))
11788        });
11789        editor.open_excerpts(&OpenExcerpts, cx);
11790    });
11791    cx.executor().run_until_parked();
11792    workspace
11793        .update(cx, |workspace, cx| {
11794            let active_item = workspace
11795                .active_item(cx)
11796                .expect("should have an active item after navigating into the 3rd buffer");
11797            let third_item_id = active_item.item_id();
11798            assert_ne!(
11799                third_item_id, multibuffer_item_id,
11800                "Should navigate into the 3rd buffer and activate it"
11801            );
11802            assert_ne!(third_item_id, first_item_id);
11803            assert_ne!(third_item_id, second_item_id);
11804            assert!(
11805                active_item.is_singleton(cx),
11806                "New active item should be a singleton buffer"
11807            );
11808            assert_eq!(
11809                active_item
11810                    .act_as::<Editor>(cx)
11811                    .expect("should have navigated into an editor")
11812                    .read(cx)
11813                    .text(cx),
11814                sample_text_3
11815            );
11816
11817            workspace
11818                .go_back(workspace.active_pane().downgrade(), cx)
11819                .detach_and_log_err(cx);
11820        })
11821        .unwrap();
11822    cx.executor().run_until_parked();
11823    workspace
11824        .update(cx, |workspace, cx| {
11825            let active_item = workspace
11826                .active_item(cx)
11827                .expect("should have an active item after navigating back from the 3rd buffer");
11828            assert_eq!(
11829                active_item.item_id(),
11830                multibuffer_item_id,
11831                "Should navigate back from the 3rd buffer to the multi buffer"
11832            );
11833            assert!(!active_item.is_singleton(cx));
11834        })
11835        .unwrap();
11836}
11837
11838#[gpui::test]
11839async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11840    init_test(cx, |_| {});
11841
11842    let mut cx = EditorTestContext::new(cx).await;
11843
11844    let diff_base = r#"
11845        use some::mod;
11846
11847        const A: u32 = 42;
11848
11849        fn main() {
11850            println!("hello");
11851
11852            println!("world");
11853        }
11854        "#
11855    .unindent();
11856
11857    cx.set_state(
11858        &r#"
11859        use some::modified;
11860
11861        ˇ
11862        fn main() {
11863            println!("hello there");
11864
11865            println!("around the");
11866            println!("world");
11867        }
11868        "#
11869        .unindent(),
11870    );
11871
11872    cx.set_diff_base(Some(&diff_base));
11873    executor.run_until_parked();
11874
11875    cx.update_editor(|editor, cx| {
11876        editor.go_to_next_hunk(&GoToHunk, cx);
11877        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11878    });
11879    executor.run_until_parked();
11880    cx.assert_diff_hunks(
11881        r#"
11882          use some::modified;
11883
11884
11885          fn main() {
11886        -     println!("hello");
11887        +     println!("hello there");
11888
11889              println!("around the");
11890              println!("world");
11891          }
11892        "#
11893        .unindent(),
11894    );
11895
11896    cx.update_editor(|editor, cx| {
11897        for _ in 0..3 {
11898            editor.go_to_next_hunk(&GoToHunk, cx);
11899            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11900        }
11901    });
11902    executor.run_until_parked();
11903    cx.assert_editor_state(
11904        &r#"
11905        use some::modified;
11906
11907        ˇ
11908        fn main() {
11909            println!("hello there");
11910
11911            println!("around the");
11912            println!("world");
11913        }
11914        "#
11915        .unindent(),
11916    );
11917
11918    cx.assert_diff_hunks(
11919        r#"
11920        - use some::mod;
11921        + use some::modified;
11922
11923        - const A: u32 = 42;
11924
11925          fn main() {
11926        -     println!("hello");
11927        +     println!("hello there");
11928
11929        +     println!("around the");
11930              println!("world");
11931          }
11932        "#
11933        .unindent(),
11934    );
11935
11936    cx.update_editor(|editor, cx| {
11937        editor.cancel(&Cancel, cx);
11938    });
11939
11940    cx.assert_diff_hunks(
11941        r#"
11942          use some::modified;
11943
11944
11945          fn main() {
11946              println!("hello there");
11947
11948              println!("around the");
11949              println!("world");
11950          }
11951        "#
11952        .unindent(),
11953    );
11954}
11955
11956#[gpui::test]
11957async fn test_diff_base_change_with_expanded_diff_hunks(
11958    executor: BackgroundExecutor,
11959    cx: &mut gpui::TestAppContext,
11960) {
11961    init_test(cx, |_| {});
11962
11963    let mut cx = EditorTestContext::new(cx).await;
11964
11965    let diff_base = r#"
11966        use some::mod1;
11967        use some::mod2;
11968
11969        const A: u32 = 42;
11970        const B: u32 = 42;
11971        const C: u32 = 42;
11972
11973        fn main() {
11974            println!("hello");
11975
11976            println!("world");
11977        }
11978        "#
11979    .unindent();
11980
11981    cx.set_state(
11982        &r#"
11983        use some::mod2;
11984
11985        const A: u32 = 42;
11986        const C: u32 = 42;
11987
11988        fn main(ˇ) {
11989            //println!("hello");
11990
11991            println!("world");
11992            //
11993            //
11994        }
11995        "#
11996        .unindent(),
11997    );
11998
11999    cx.set_diff_base(Some(&diff_base));
12000    executor.run_until_parked();
12001
12002    cx.update_editor(|editor, cx| {
12003        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12004    });
12005    executor.run_until_parked();
12006    cx.assert_diff_hunks(
12007        r#"
12008        - use some::mod1;
12009          use some::mod2;
12010
12011          const A: u32 = 42;
12012        - const B: u32 = 42;
12013          const C: u32 = 42;
12014
12015          fn main() {
12016        -     println!("hello");
12017        +     //println!("hello");
12018
12019              println!("world");
12020        +     //
12021        +     //
12022          }
12023        "#
12024        .unindent(),
12025    );
12026
12027    cx.set_diff_base(Some("new diff base!"));
12028    executor.run_until_parked();
12029    cx.assert_diff_hunks(
12030        r#"
12031          use some::mod2;
12032
12033          const A: u32 = 42;
12034          const C: u32 = 42;
12035
12036          fn main() {
12037              //println!("hello");
12038
12039              println!("world");
12040              //
12041              //
12042          }
12043        "#
12044        .unindent(),
12045    );
12046
12047    cx.update_editor(|editor, cx| {
12048        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12049    });
12050    executor.run_until_parked();
12051    cx.assert_diff_hunks(
12052        r#"
12053        - new diff base!
12054        + use some::mod2;
12055        +
12056        + const A: u32 = 42;
12057        + const C: u32 = 42;
12058        +
12059        + fn main() {
12060        +     //println!("hello");
12061        +
12062        +     println!("world");
12063        +     //
12064        +     //
12065        + }
12066        "#
12067        .unindent(),
12068    );
12069}
12070
12071#[gpui::test]
12072async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12073    init_test(cx, |_| {});
12074
12075    let mut cx = EditorTestContext::new(cx).await;
12076
12077    let diff_base = r#"
12078        use some::mod1;
12079        use some::mod2;
12080
12081        const A: u32 = 42;
12082        const B: u32 = 42;
12083        const C: u32 = 42;
12084
12085        fn main() {
12086            println!("hello");
12087
12088            println!("world");
12089        }
12090
12091        fn another() {
12092            println!("another");
12093        }
12094
12095        fn another2() {
12096            println!("another2");
12097        }
12098        "#
12099    .unindent();
12100
12101    cx.set_state(
12102        &r#"
12103        «use some::mod2;
12104
12105        const A: u32 = 42;
12106        const C: u32 = 42;
12107
12108        fn main() {
12109            //println!("hello");
12110
12111            println!("world");
12112            //
12113            //ˇ»
12114        }
12115
12116        fn another() {
12117            println!("another");
12118            println!("another");
12119        }
12120
12121            println!("another2");
12122        }
12123        "#
12124        .unindent(),
12125    );
12126
12127    cx.set_diff_base(Some(&diff_base));
12128    executor.run_until_parked();
12129
12130    cx.update_editor(|editor, cx| {
12131        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12132    });
12133    executor.run_until_parked();
12134
12135    cx.assert_diff_hunks(
12136        r#"
12137        - use some::mod1;
12138          use some::mod2;
12139
12140          const A: u32 = 42;
12141        - const B: u32 = 42;
12142          const C: u32 = 42;
12143
12144          fn main() {
12145        -     println!("hello");
12146        +     //println!("hello");
12147
12148              println!("world");
12149        +     //
12150        +     //
12151          }
12152
12153          fn another() {
12154              println!("another");
12155        +     println!("another");
12156          }
12157
12158        - fn another2() {
12159              println!("another2");
12160          }
12161        "#
12162        .unindent(),
12163    );
12164
12165    // Fold across some of the diff hunks. They should no longer appear expanded.
12166    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12167    cx.executor().run_until_parked();
12168
12169    // Hunks are not shown if their position is within a fold
12170    cx.assert_diff_hunks(
12171        r#"
12172          use some::mod2;
12173
12174          const A: u32 = 42;
12175          const C: u32 = 42;
12176
12177          fn main() {
12178              //println!("hello");
12179
12180              println!("world");
12181              //
12182              //
12183          }
12184
12185          fn another() {
12186              println!("another");
12187        +     println!("another");
12188          }
12189
12190        - fn another2() {
12191              println!("another2");
12192          }
12193        "#
12194        .unindent(),
12195    );
12196
12197    cx.update_editor(|editor, cx| {
12198        editor.select_all(&SelectAll, cx);
12199        editor.unfold_lines(&UnfoldLines, cx);
12200    });
12201    cx.executor().run_until_parked();
12202
12203    // The deletions reappear when unfolding.
12204    cx.assert_diff_hunks(
12205        r#"
12206        - use some::mod1;
12207          use some::mod2;
12208
12209          const A: u32 = 42;
12210        - const B: u32 = 42;
12211          const C: u32 = 42;
12212
12213          fn main() {
12214        -     println!("hello");
12215        +     //println!("hello");
12216
12217              println!("world");
12218        +     //
12219        +     //
12220          }
12221
12222          fn another() {
12223              println!("another");
12224        +     println!("another");
12225          }
12226
12227        - fn another2() {
12228              println!("another2");
12229          }
12230        "#
12231        .unindent(),
12232    );
12233}
12234
12235#[gpui::test]
12236async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12237    init_test(cx, |_| {});
12238
12239    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12240    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12241    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12242    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12243    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12244    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12245
12246    let buffer_1 = cx.new_model(|cx| {
12247        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
12248        buffer.set_diff_base(Some(file_1_old.into()), cx);
12249        buffer
12250    });
12251    let buffer_2 = cx.new_model(|cx| {
12252        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
12253        buffer.set_diff_base(Some(file_2_old.into()), cx);
12254        buffer
12255    });
12256    let buffer_3 = cx.new_model(|cx| {
12257        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
12258        buffer.set_diff_base(Some(file_3_old.into()), cx);
12259        buffer
12260    });
12261
12262    let multi_buffer = cx.new_model(|cx| {
12263        let mut multibuffer = MultiBuffer::new(ReadWrite);
12264        multibuffer.push_excerpts(
12265            buffer_1.clone(),
12266            [
12267                ExcerptRange {
12268                    context: Point::new(0, 0)..Point::new(3, 0),
12269                    primary: None,
12270                },
12271                ExcerptRange {
12272                    context: Point::new(5, 0)..Point::new(7, 0),
12273                    primary: None,
12274                },
12275                ExcerptRange {
12276                    context: Point::new(9, 0)..Point::new(10, 3),
12277                    primary: None,
12278                },
12279            ],
12280            cx,
12281        );
12282        multibuffer.push_excerpts(
12283            buffer_2.clone(),
12284            [
12285                ExcerptRange {
12286                    context: Point::new(0, 0)..Point::new(3, 0),
12287                    primary: None,
12288                },
12289                ExcerptRange {
12290                    context: Point::new(5, 0)..Point::new(7, 0),
12291                    primary: None,
12292                },
12293                ExcerptRange {
12294                    context: Point::new(9, 0)..Point::new(10, 3),
12295                    primary: None,
12296                },
12297            ],
12298            cx,
12299        );
12300        multibuffer.push_excerpts(
12301            buffer_3.clone(),
12302            [
12303                ExcerptRange {
12304                    context: Point::new(0, 0)..Point::new(3, 0),
12305                    primary: None,
12306                },
12307                ExcerptRange {
12308                    context: Point::new(5, 0)..Point::new(7, 0),
12309                    primary: None,
12310                },
12311                ExcerptRange {
12312                    context: Point::new(9, 0)..Point::new(10, 3),
12313                    primary: None,
12314                },
12315            ],
12316            cx,
12317        );
12318        multibuffer
12319    });
12320
12321    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12322    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12323    cx.run_until_parked();
12324
12325    cx.assert_editor_state(
12326        &"
12327            ˇaaa
12328            ccc
12329            ddd
12330
12331            ggg
12332            hhh
12333
12334
12335            lll
12336            mmm
12337            NNN
12338
12339            qqq
12340            rrr
12341
12342            uuu
12343            111
12344            222
12345            333
12346
12347            666
12348            777
12349
12350            000
12351            !!!"
12352        .unindent(),
12353    );
12354
12355    cx.update_editor(|editor, cx| {
12356        editor.select_all(&SelectAll, cx);
12357        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12358    });
12359    cx.executor().run_until_parked();
12360
12361    cx.assert_diff_hunks(
12362        "
12363            aaa
12364          - bbb
12365            ccc
12366            ddd
12367
12368            ggg
12369            hhh
12370
12371
12372            lll
12373            mmm
12374          - nnn
12375          + NNN
12376
12377            qqq
12378            rrr
12379
12380            uuu
12381            111
12382            222
12383            333
12384
12385          + 666
12386            777
12387
12388            000
12389            !!!"
12390        .unindent(),
12391    );
12392}
12393
12394#[gpui::test]
12395async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12396    init_test(cx, |_| {});
12397
12398    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12399    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12400
12401    let buffer = cx.new_model(|cx| {
12402        let mut buffer = Buffer::local(text.to_string(), cx);
12403        buffer.set_diff_base(Some(base.into()), cx);
12404        buffer
12405    });
12406
12407    let multi_buffer = cx.new_model(|cx| {
12408        let mut multibuffer = MultiBuffer::new(ReadWrite);
12409        multibuffer.push_excerpts(
12410            buffer.clone(),
12411            [
12412                ExcerptRange {
12413                    context: Point::new(0, 0)..Point::new(2, 0),
12414                    primary: None,
12415                },
12416                ExcerptRange {
12417                    context: Point::new(5, 0)..Point::new(7, 0),
12418                    primary: None,
12419                },
12420            ],
12421            cx,
12422        );
12423        multibuffer
12424    });
12425
12426    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12427    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12428    cx.run_until_parked();
12429
12430    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12431    cx.executor().run_until_parked();
12432
12433    cx.assert_diff_hunks(
12434        "
12435            aaa
12436          - bbb
12437          + BBB
12438
12439          - ddd
12440          - eee
12441          + EEE
12442            fff
12443        "
12444        .unindent(),
12445    );
12446}
12447
12448#[gpui::test]
12449async fn test_edits_around_expanded_insertion_hunks(
12450    executor: BackgroundExecutor,
12451    cx: &mut gpui::TestAppContext,
12452) {
12453    init_test(cx, |_| {});
12454
12455    let mut cx = EditorTestContext::new(cx).await;
12456
12457    let diff_base = r#"
12458        use some::mod1;
12459        use some::mod2;
12460
12461        const A: u32 = 42;
12462
12463        fn main() {
12464            println!("hello");
12465
12466            println!("world");
12467        }
12468        "#
12469    .unindent();
12470    executor.run_until_parked();
12471    cx.set_state(
12472        &r#"
12473        use some::mod1;
12474        use some::mod2;
12475
12476        const A: u32 = 42;
12477        const B: u32 = 42;
12478        const C: u32 = 42;
12479        ˇ
12480
12481        fn main() {
12482            println!("hello");
12483
12484            println!("world");
12485        }
12486        "#
12487        .unindent(),
12488    );
12489
12490    cx.set_diff_base(Some(&diff_base));
12491    executor.run_until_parked();
12492
12493    cx.update_editor(|editor, cx| {
12494        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12495    });
12496    executor.run_until_parked();
12497
12498    cx.assert_diff_hunks(
12499        r#"
12500        use some::mod1;
12501        use some::mod2;
12502
12503        const A: u32 = 42;
12504      + const B: u32 = 42;
12505      + const C: u32 = 42;
12506      +
12507
12508        fn main() {
12509            println!("hello");
12510
12511            println!("world");
12512        }
12513        "#
12514        .unindent(),
12515    );
12516
12517    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12518    executor.run_until_parked();
12519
12520    cx.assert_diff_hunks(
12521        r#"
12522        use some::mod1;
12523        use some::mod2;
12524
12525        const A: u32 = 42;
12526      + const B: u32 = 42;
12527      + const C: u32 = 42;
12528      + const D: u32 = 42;
12529      +
12530
12531        fn main() {
12532            println!("hello");
12533
12534            println!("world");
12535        }
12536        "#
12537        .unindent(),
12538    );
12539
12540    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12541    executor.run_until_parked();
12542
12543    cx.assert_diff_hunks(
12544        r#"
12545        use some::mod1;
12546        use some::mod2;
12547
12548        const A: u32 = 42;
12549      + const B: u32 = 42;
12550      + const C: u32 = 42;
12551      + const D: u32 = 42;
12552      + const E: u32 = 42;
12553      +
12554
12555        fn main() {
12556            println!("hello");
12557
12558            println!("world");
12559        }
12560        "#
12561        .unindent(),
12562    );
12563
12564    cx.update_editor(|editor, cx| {
12565        editor.delete_line(&DeleteLine, cx);
12566    });
12567    executor.run_until_parked();
12568
12569    cx.assert_diff_hunks(
12570        r#"
12571        use some::mod1;
12572        use some::mod2;
12573
12574        const A: u32 = 42;
12575      + const B: u32 = 42;
12576      + const C: u32 = 42;
12577      + const D: u32 = 42;
12578      + const E: u32 = 42;
12579
12580        fn main() {
12581            println!("hello");
12582
12583            println!("world");
12584        }
12585        "#
12586        .unindent(),
12587    );
12588
12589    cx.update_editor(|editor, cx| {
12590        editor.move_up(&MoveUp, cx);
12591        editor.delete_line(&DeleteLine, cx);
12592        editor.move_up(&MoveUp, cx);
12593        editor.delete_line(&DeleteLine, cx);
12594        editor.move_up(&MoveUp, cx);
12595        editor.delete_line(&DeleteLine, cx);
12596    });
12597    executor.run_until_parked();
12598    cx.assert_editor_state(
12599        &r#"
12600        use some::mod1;
12601        use some::mod2;
12602
12603        const A: u32 = 42;
12604        const B: u32 = 42;
12605        ˇ
12606        fn main() {
12607            println!("hello");
12608
12609            println!("world");
12610        }
12611        "#
12612        .unindent(),
12613    );
12614
12615    cx.assert_diff_hunks(
12616        r#"
12617        use some::mod1;
12618        use some::mod2;
12619
12620        const A: u32 = 42;
12621      + const B: u32 = 42;
12622
12623        fn main() {
12624            println!("hello");
12625
12626            println!("world");
12627        }
12628        "#
12629        .unindent(),
12630    );
12631
12632    cx.update_editor(|editor, cx| {
12633        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12634        editor.delete_line(&DeleteLine, cx);
12635    });
12636    executor.run_until_parked();
12637    cx.assert_diff_hunks(
12638        r#"
12639        use some::mod1;
12640      - use some::mod2;
12641      -
12642      - const A: u32 = 42;
12643
12644        fn main() {
12645            println!("hello");
12646
12647            println!("world");
12648        }
12649        "#
12650        .unindent(),
12651    );
12652}
12653
12654#[gpui::test]
12655async fn test_edits_around_expanded_deletion_hunks(
12656    executor: BackgroundExecutor,
12657    cx: &mut gpui::TestAppContext,
12658) {
12659    init_test(cx, |_| {});
12660
12661    let mut cx = EditorTestContext::new(cx).await;
12662
12663    let diff_base = r#"
12664        use some::mod1;
12665        use some::mod2;
12666
12667        const A: u32 = 42;
12668        const B: u32 = 42;
12669        const C: u32 = 42;
12670
12671
12672        fn main() {
12673            println!("hello");
12674
12675            println!("world");
12676        }
12677    "#
12678    .unindent();
12679    executor.run_until_parked();
12680    cx.set_state(
12681        &r#"
12682        use some::mod1;
12683        use some::mod2;
12684
12685        ˇconst B: u32 = 42;
12686        const C: u32 = 42;
12687
12688
12689        fn main() {
12690            println!("hello");
12691
12692            println!("world");
12693        }
12694        "#
12695        .unindent(),
12696    );
12697
12698    cx.set_diff_base(Some(&diff_base));
12699    executor.run_until_parked();
12700
12701    cx.update_editor(|editor, cx| {
12702        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12703    });
12704    executor.run_until_parked();
12705
12706    cx.assert_diff_hunks(
12707        r#"
12708        use some::mod1;
12709        use some::mod2;
12710
12711      - const A: u32 = 42;
12712        const B: u32 = 42;
12713        const C: u32 = 42;
12714
12715
12716        fn main() {
12717            println!("hello");
12718
12719            println!("world");
12720        }
12721        "#
12722        .unindent(),
12723    );
12724
12725    cx.update_editor(|editor, cx| {
12726        editor.delete_line(&DeleteLine, cx);
12727    });
12728    executor.run_until_parked();
12729    cx.assert_editor_state(
12730        &r#"
12731        use some::mod1;
12732        use some::mod2;
12733
12734        ˇconst C: u32 = 42;
12735
12736
12737        fn main() {
12738            println!("hello");
12739
12740            println!("world");
12741        }
12742        "#
12743        .unindent(),
12744    );
12745    cx.assert_diff_hunks(
12746        r#"
12747        use some::mod1;
12748        use some::mod2;
12749
12750      - const A: u32 = 42;
12751      - const B: u32 = 42;
12752        const C: u32 = 42;
12753
12754
12755        fn main() {
12756            println!("hello");
12757
12758            println!("world");
12759        }
12760        "#
12761        .unindent(),
12762    );
12763
12764    cx.update_editor(|editor, cx| {
12765        editor.delete_line(&DeleteLine, cx);
12766    });
12767    executor.run_until_parked();
12768    cx.assert_editor_state(
12769        &r#"
12770        use some::mod1;
12771        use some::mod2;
12772
12773        ˇ
12774
12775        fn main() {
12776            println!("hello");
12777
12778            println!("world");
12779        }
12780        "#
12781        .unindent(),
12782    );
12783    cx.assert_diff_hunks(
12784        r#"
12785        use some::mod1;
12786        use some::mod2;
12787
12788      - const A: u32 = 42;
12789      - const B: u32 = 42;
12790      - const C: u32 = 42;
12791
12792
12793        fn main() {
12794            println!("hello");
12795
12796            println!("world");
12797        }
12798        "#
12799        .unindent(),
12800    );
12801
12802    cx.update_editor(|editor, cx| {
12803        editor.handle_input("replacement", cx);
12804    });
12805    executor.run_until_parked();
12806    cx.assert_editor_state(
12807        &r#"
12808        use some::mod1;
12809        use some::mod2;
12810
12811        replacementˇ
12812
12813        fn main() {
12814            println!("hello");
12815
12816            println!("world");
12817        }
12818        "#
12819        .unindent(),
12820    );
12821    cx.assert_diff_hunks(
12822        r#"
12823        use some::mod1;
12824        use some::mod2;
12825
12826      - const A: u32 = 42;
12827      - const B: u32 = 42;
12828      - const C: u32 = 42;
12829      -
12830      + replacement
12831
12832        fn main() {
12833            println!("hello");
12834
12835            println!("world");
12836        }
12837        "#
12838        .unindent(),
12839    );
12840}
12841
12842#[gpui::test]
12843async fn test_edit_after_expanded_modification_hunk(
12844    executor: BackgroundExecutor,
12845    cx: &mut gpui::TestAppContext,
12846) {
12847    init_test(cx, |_| {});
12848
12849    let mut cx = EditorTestContext::new(cx).await;
12850
12851    let diff_base = r#"
12852        use some::mod1;
12853        use some::mod2;
12854
12855        const A: u32 = 42;
12856        const B: u32 = 42;
12857        const C: u32 = 42;
12858        const D: u32 = 42;
12859
12860
12861        fn main() {
12862            println!("hello");
12863
12864            println!("world");
12865        }"#
12866    .unindent();
12867
12868    cx.set_state(
12869        &r#"
12870        use some::mod1;
12871        use some::mod2;
12872
12873        const A: u32 = 42;
12874        const B: u32 = 42;
12875        const C: u32 = 43ˇ
12876        const D: u32 = 42;
12877
12878
12879        fn main() {
12880            println!("hello");
12881
12882            println!("world");
12883        }"#
12884        .unindent(),
12885    );
12886
12887    cx.set_diff_base(Some(&diff_base));
12888    executor.run_until_parked();
12889    cx.update_editor(|editor, cx| {
12890        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12891    });
12892    executor.run_until_parked();
12893
12894    cx.assert_diff_hunks(
12895        r#"
12896        use some::mod1;
12897        use some::mod2;
12898
12899        const A: u32 = 42;
12900        const B: u32 = 42;
12901      - const C: u32 = 42;
12902      + const C: u32 = 43
12903        const D: u32 = 42;
12904
12905
12906        fn main() {
12907            println!("hello");
12908
12909            println!("world");
12910        }"#
12911        .unindent(),
12912    );
12913
12914    cx.update_editor(|editor, cx| {
12915        editor.handle_input("\nnew_line\n", cx);
12916    });
12917    executor.run_until_parked();
12918
12919    cx.assert_diff_hunks(
12920        r#"
12921        use some::mod1;
12922        use some::mod2;
12923
12924        const A: u32 = 42;
12925        const B: u32 = 42;
12926      - const C: u32 = 42;
12927      + const C: u32 = 43
12928      + new_line
12929      +
12930        const D: u32 = 42;
12931
12932
12933        fn main() {
12934            println!("hello");
12935
12936            println!("world");
12937        }"#
12938        .unindent(),
12939    );
12940}
12941
12942async fn setup_indent_guides_editor(
12943    text: &str,
12944    cx: &mut gpui::TestAppContext,
12945) -> (BufferId, EditorTestContext) {
12946    init_test(cx, |_| {});
12947
12948    let mut cx = EditorTestContext::new(cx).await;
12949
12950    let buffer_id = cx.update_editor(|editor, cx| {
12951        editor.set_text(text, cx);
12952        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12953
12954        buffer_ids[0]
12955    });
12956
12957    (buffer_id, cx)
12958}
12959
12960fn assert_indent_guides(
12961    range: Range<u32>,
12962    expected: Vec<IndentGuide>,
12963    active_indices: Option<Vec<usize>>,
12964    cx: &mut EditorTestContext,
12965) {
12966    let indent_guides = cx.update_editor(|editor, cx| {
12967        let snapshot = editor.snapshot(cx).display_snapshot;
12968        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12969            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12970            true,
12971            &snapshot,
12972            cx,
12973        );
12974
12975        indent_guides.sort_by(|a, b| {
12976            a.depth.cmp(&b.depth).then(
12977                a.start_row
12978                    .cmp(&b.start_row)
12979                    .then(a.end_row.cmp(&b.end_row)),
12980            )
12981        });
12982        indent_guides
12983    });
12984
12985    if let Some(expected) = active_indices {
12986        let active_indices = cx.update_editor(|editor, cx| {
12987            let snapshot = editor.snapshot(cx).display_snapshot;
12988            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12989        });
12990
12991        assert_eq!(
12992            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12993            expected,
12994            "Active indent guide indices do not match"
12995        );
12996    }
12997
12998    let expected: Vec<_> = expected
12999        .into_iter()
13000        .map(|guide| MultiBufferIndentGuide {
13001            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13002            buffer: guide,
13003        })
13004        .collect();
13005
13006    assert_eq!(indent_guides, expected, "Indent guides do not match");
13007}
13008
13009fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13010    IndentGuide {
13011        buffer_id,
13012        start_row,
13013        end_row,
13014        depth,
13015        tab_size: 4,
13016        settings: IndentGuideSettings {
13017            enabled: true,
13018            line_width: 1,
13019            active_line_width: 1,
13020            ..Default::default()
13021        },
13022    }
13023}
13024
13025#[gpui::test]
13026async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13027    let (buffer_id, mut cx) = setup_indent_guides_editor(
13028        &"
13029    fn main() {
13030        let a = 1;
13031    }"
13032        .unindent(),
13033        cx,
13034    )
13035    .await;
13036
13037    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13038}
13039
13040#[gpui::test]
13041async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13042    let (buffer_id, mut cx) = setup_indent_guides_editor(
13043        &"
13044    fn main() {
13045        let a = 1;
13046        let b = 2;
13047    }"
13048        .unindent(),
13049        cx,
13050    )
13051    .await;
13052
13053    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13054}
13055
13056#[gpui::test]
13057async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13058    let (buffer_id, mut cx) = setup_indent_guides_editor(
13059        &"
13060    fn main() {
13061        let a = 1;
13062        if a == 3 {
13063            let b = 2;
13064        } else {
13065            let c = 3;
13066        }
13067    }"
13068        .unindent(),
13069        cx,
13070    )
13071    .await;
13072
13073    assert_indent_guides(
13074        0..8,
13075        vec![
13076            indent_guide(buffer_id, 1, 6, 0),
13077            indent_guide(buffer_id, 3, 3, 1),
13078            indent_guide(buffer_id, 5, 5, 1),
13079        ],
13080        None,
13081        &mut cx,
13082    );
13083}
13084
13085#[gpui::test]
13086async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13087    let (buffer_id, mut cx) = setup_indent_guides_editor(
13088        &"
13089    fn main() {
13090        let a = 1;
13091            let b = 2;
13092        let c = 3;
13093    }"
13094        .unindent(),
13095        cx,
13096    )
13097    .await;
13098
13099    assert_indent_guides(
13100        0..5,
13101        vec![
13102            indent_guide(buffer_id, 1, 3, 0),
13103            indent_guide(buffer_id, 2, 2, 1),
13104        ],
13105        None,
13106        &mut cx,
13107    );
13108}
13109
13110#[gpui::test]
13111async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13112    let (buffer_id, mut cx) = setup_indent_guides_editor(
13113        &"
13114        fn main() {
13115            let a = 1;
13116
13117            let c = 3;
13118        }"
13119        .unindent(),
13120        cx,
13121    )
13122    .await;
13123
13124    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13125}
13126
13127#[gpui::test]
13128async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13129    let (buffer_id, mut cx) = setup_indent_guides_editor(
13130        &"
13131        fn main() {
13132            let a = 1;
13133
13134            let c = 3;
13135
13136            if a == 3 {
13137                let b = 2;
13138            } else {
13139                let c = 3;
13140            }
13141        }"
13142        .unindent(),
13143        cx,
13144    )
13145    .await;
13146
13147    assert_indent_guides(
13148        0..11,
13149        vec![
13150            indent_guide(buffer_id, 1, 9, 0),
13151            indent_guide(buffer_id, 6, 6, 1),
13152            indent_guide(buffer_id, 8, 8, 1),
13153        ],
13154        None,
13155        &mut cx,
13156    );
13157}
13158
13159#[gpui::test]
13160async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13161    let (buffer_id, mut cx) = setup_indent_guides_editor(
13162        &"
13163        fn main() {
13164            let a = 1;
13165
13166            let c = 3;
13167
13168            if a == 3 {
13169                let b = 2;
13170            } else {
13171                let c = 3;
13172            }
13173        }"
13174        .unindent(),
13175        cx,
13176    )
13177    .await;
13178
13179    assert_indent_guides(
13180        1..11,
13181        vec![
13182            indent_guide(buffer_id, 1, 9, 0),
13183            indent_guide(buffer_id, 6, 6, 1),
13184            indent_guide(buffer_id, 8, 8, 1),
13185        ],
13186        None,
13187        &mut cx,
13188    );
13189}
13190
13191#[gpui::test]
13192async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13193    let (buffer_id, mut cx) = setup_indent_guides_editor(
13194        &"
13195        fn main() {
13196            let a = 1;
13197
13198            let c = 3;
13199
13200            if a == 3 {
13201                let b = 2;
13202            } else {
13203                let c = 3;
13204            }
13205        }"
13206        .unindent(),
13207        cx,
13208    )
13209    .await;
13210
13211    assert_indent_guides(
13212        1..10,
13213        vec![
13214            indent_guide(buffer_id, 1, 9, 0),
13215            indent_guide(buffer_id, 6, 6, 1),
13216            indent_guide(buffer_id, 8, 8, 1),
13217        ],
13218        None,
13219        &mut cx,
13220    );
13221}
13222
13223#[gpui::test]
13224async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13225    let (buffer_id, mut cx) = setup_indent_guides_editor(
13226        &"
13227        block1
13228            block2
13229                block3
13230                    block4
13231            block2
13232        block1
13233        block1"
13234            .unindent(),
13235        cx,
13236    )
13237    .await;
13238
13239    assert_indent_guides(
13240        1..10,
13241        vec![
13242            indent_guide(buffer_id, 1, 4, 0),
13243            indent_guide(buffer_id, 2, 3, 1),
13244            indent_guide(buffer_id, 3, 3, 2),
13245        ],
13246        None,
13247        &mut cx,
13248    );
13249}
13250
13251#[gpui::test]
13252async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13253    let (buffer_id, mut cx) = setup_indent_guides_editor(
13254        &"
13255        block1
13256            block2
13257                block3
13258
13259        block1
13260        block1"
13261            .unindent(),
13262        cx,
13263    )
13264    .await;
13265
13266    assert_indent_guides(
13267        0..6,
13268        vec![
13269            indent_guide(buffer_id, 1, 2, 0),
13270            indent_guide(buffer_id, 2, 2, 1),
13271        ],
13272        None,
13273        &mut cx,
13274    );
13275}
13276
13277#[gpui::test]
13278async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13279    let (buffer_id, mut cx) = setup_indent_guides_editor(
13280        &"
13281        block1
13282
13283
13284
13285            block2
13286        "
13287        .unindent(),
13288        cx,
13289    )
13290    .await;
13291
13292    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13293}
13294
13295#[gpui::test]
13296async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13297    let (buffer_id, mut cx) = setup_indent_guides_editor(
13298        &"
13299        def a:
13300        \tb = 3
13301        \tif True:
13302        \t\tc = 4
13303        \t\td = 5
13304        \tprint(b)
13305        "
13306        .unindent(),
13307        cx,
13308    )
13309    .await;
13310
13311    assert_indent_guides(
13312        0..6,
13313        vec![
13314            indent_guide(buffer_id, 1, 6, 0),
13315            indent_guide(buffer_id, 3, 4, 1),
13316        ],
13317        None,
13318        &mut cx,
13319    );
13320}
13321
13322#[gpui::test]
13323async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13324    let (buffer_id, mut cx) = setup_indent_guides_editor(
13325        &"
13326    fn main() {
13327        let a = 1;
13328    }"
13329        .unindent(),
13330        cx,
13331    )
13332    .await;
13333
13334    cx.update_editor(|editor, cx| {
13335        editor.change_selections(None, cx, |s| {
13336            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13337        });
13338    });
13339
13340    assert_indent_guides(
13341        0..3,
13342        vec![indent_guide(buffer_id, 1, 1, 0)],
13343        Some(vec![0]),
13344        &mut cx,
13345    );
13346}
13347
13348#[gpui::test]
13349async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13350    let (buffer_id, mut cx) = setup_indent_guides_editor(
13351        &"
13352    fn main() {
13353        if 1 == 2 {
13354            let a = 1;
13355        }
13356    }"
13357        .unindent(),
13358        cx,
13359    )
13360    .await;
13361
13362    cx.update_editor(|editor, cx| {
13363        editor.change_selections(None, cx, |s| {
13364            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13365        });
13366    });
13367
13368    assert_indent_guides(
13369        0..4,
13370        vec![
13371            indent_guide(buffer_id, 1, 3, 0),
13372            indent_guide(buffer_id, 2, 2, 1),
13373        ],
13374        Some(vec![1]),
13375        &mut cx,
13376    );
13377
13378    cx.update_editor(|editor, cx| {
13379        editor.change_selections(None, cx, |s| {
13380            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13381        });
13382    });
13383
13384    assert_indent_guides(
13385        0..4,
13386        vec![
13387            indent_guide(buffer_id, 1, 3, 0),
13388            indent_guide(buffer_id, 2, 2, 1),
13389        ],
13390        Some(vec![1]),
13391        &mut cx,
13392    );
13393
13394    cx.update_editor(|editor, cx| {
13395        editor.change_selections(None, cx, |s| {
13396            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13397        });
13398    });
13399
13400    assert_indent_guides(
13401        0..4,
13402        vec![
13403            indent_guide(buffer_id, 1, 3, 0),
13404            indent_guide(buffer_id, 2, 2, 1),
13405        ],
13406        Some(vec![0]),
13407        &mut cx,
13408    );
13409}
13410
13411#[gpui::test]
13412async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13413    let (buffer_id, mut cx) = setup_indent_guides_editor(
13414        &"
13415    fn main() {
13416        let a = 1;
13417
13418        let b = 2;
13419    }"
13420        .unindent(),
13421        cx,
13422    )
13423    .await;
13424
13425    cx.update_editor(|editor, cx| {
13426        editor.change_selections(None, cx, |s| {
13427            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13428        });
13429    });
13430
13431    assert_indent_guides(
13432        0..5,
13433        vec![indent_guide(buffer_id, 1, 3, 0)],
13434        Some(vec![0]),
13435        &mut cx,
13436    );
13437}
13438
13439#[gpui::test]
13440async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13441    let (buffer_id, mut cx) = setup_indent_guides_editor(
13442        &"
13443    def m:
13444        a = 1
13445        pass"
13446            .unindent(),
13447        cx,
13448    )
13449    .await;
13450
13451    cx.update_editor(|editor, cx| {
13452        editor.change_selections(None, cx, |s| {
13453            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13454        });
13455    });
13456
13457    assert_indent_guides(
13458        0..3,
13459        vec![indent_guide(buffer_id, 1, 2, 0)],
13460        Some(vec![0]),
13461        &mut cx,
13462    );
13463}
13464
13465#[gpui::test]
13466fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13467    init_test(cx, |_| {});
13468
13469    let editor = cx.add_window(|cx| {
13470        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13471        build_editor(buffer, cx)
13472    });
13473
13474    let render_args = Arc::new(Mutex::new(None));
13475    let snapshot = editor
13476        .update(cx, |editor, cx| {
13477            let snapshot = editor.buffer().read(cx).snapshot(cx);
13478            let range =
13479                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13480
13481            struct RenderArgs {
13482                row: MultiBufferRow,
13483                folded: bool,
13484                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13485            }
13486
13487            let crease = Crease::inline(
13488                range,
13489                FoldPlaceholder::test(),
13490                {
13491                    let toggle_callback = render_args.clone();
13492                    move |row, folded, callback, _cx| {
13493                        *toggle_callback.lock() = Some(RenderArgs {
13494                            row,
13495                            folded,
13496                            callback,
13497                        });
13498                        div()
13499                    }
13500                },
13501                |_row, _folded, _cx| div(),
13502            );
13503
13504            editor.insert_creases(Some(crease), cx);
13505            let snapshot = editor.snapshot(cx);
13506            let _div =
13507                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13508            snapshot
13509        })
13510        .unwrap();
13511
13512    let render_args = render_args.lock().take().unwrap();
13513    assert_eq!(render_args.row, MultiBufferRow(1));
13514    assert!(!render_args.folded);
13515    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13516
13517    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13518        .unwrap();
13519    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13520    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13521
13522    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13523        .unwrap();
13524    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13525    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13526}
13527
13528#[gpui::test]
13529async fn test_input_text(cx: &mut gpui::TestAppContext) {
13530    init_test(cx, |_| {});
13531    let mut cx = EditorTestContext::new(cx).await;
13532
13533    cx.set_state(
13534        &r#"ˇone
13535        two
13536
13537        three
13538        fourˇ
13539        five
13540
13541        siˇx"#
13542            .unindent(),
13543    );
13544
13545    cx.dispatch_action(HandleInput(String::new()));
13546    cx.assert_editor_state(
13547        &r#"ˇone
13548        two
13549
13550        three
13551        fourˇ
13552        five
13553
13554        siˇx"#
13555            .unindent(),
13556    );
13557
13558    cx.dispatch_action(HandleInput("AAAA".to_string()));
13559    cx.assert_editor_state(
13560        &r#"AAAAˇone
13561        two
13562
13563        three
13564        fourAAAAˇ
13565        five
13566
13567        siAAAAˇx"#
13568            .unindent(),
13569    );
13570}
13571
13572#[gpui::test]
13573async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13574    init_test(cx, |_| {});
13575
13576    let mut cx = EditorTestContext::new(cx).await;
13577    cx.set_state(
13578        r#"let foo = 1;
13579let foo = 2;
13580let foo = 3;
13581let fooˇ = 4;
13582let foo = 5;
13583let foo = 6;
13584let foo = 7;
13585let foo = 8;
13586let foo = 9;
13587let foo = 10;
13588let foo = 11;
13589let foo = 12;
13590let foo = 13;
13591let foo = 14;
13592let foo = 15;"#,
13593    );
13594
13595    cx.update_editor(|e, cx| {
13596        assert_eq!(
13597            e.next_scroll_position,
13598            NextScrollCursorCenterTopBottom::Center,
13599            "Default next scroll direction is center",
13600        );
13601
13602        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13603        assert_eq!(
13604            e.next_scroll_position,
13605            NextScrollCursorCenterTopBottom::Top,
13606            "After center, next scroll direction should be top",
13607        );
13608
13609        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13610        assert_eq!(
13611            e.next_scroll_position,
13612            NextScrollCursorCenterTopBottom::Bottom,
13613            "After top, next scroll direction should be bottom",
13614        );
13615
13616        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13617        assert_eq!(
13618            e.next_scroll_position,
13619            NextScrollCursorCenterTopBottom::Center,
13620            "After bottom, scrolling should start over",
13621        );
13622
13623        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13624        assert_eq!(
13625            e.next_scroll_position,
13626            NextScrollCursorCenterTopBottom::Top,
13627            "Scrolling continues if retriggered fast enough"
13628        );
13629    });
13630
13631    cx.executor()
13632        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13633    cx.executor().run_until_parked();
13634    cx.update_editor(|e, _| {
13635        assert_eq!(
13636            e.next_scroll_position,
13637            NextScrollCursorCenterTopBottom::Center,
13638            "If scrolling is not triggered fast enough, it should reset"
13639        );
13640    });
13641}
13642
13643#[gpui::test]
13644async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13645    init_test(cx, |_| {});
13646    let mut cx = EditorLspTestContext::new_rust(
13647        lsp::ServerCapabilities {
13648            definition_provider: Some(lsp::OneOf::Left(true)),
13649            references_provider: Some(lsp::OneOf::Left(true)),
13650            ..lsp::ServerCapabilities::default()
13651        },
13652        cx,
13653    )
13654    .await;
13655
13656    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13657        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13658            move |params, _| async move {
13659                if empty_go_to_definition {
13660                    Ok(None)
13661                } else {
13662                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13663                        uri: params.text_document_position_params.text_document.uri,
13664                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13665                    })))
13666                }
13667            },
13668        );
13669        let references =
13670            cx.lsp
13671                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13672                    Ok(Some(vec![lsp::Location {
13673                        uri: params.text_document_position.text_document.uri,
13674                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13675                    }]))
13676                });
13677        (go_to_definition, references)
13678    };
13679
13680    cx.set_state(
13681        &r#"fn one() {
13682            let mut a = ˇtwo();
13683        }
13684
13685        fn two() {}"#
13686            .unindent(),
13687    );
13688    set_up_lsp_handlers(false, &mut cx);
13689    let navigated = cx
13690        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13691        .await
13692        .expect("Failed to navigate to definition");
13693    assert_eq!(
13694        navigated,
13695        Navigated::Yes,
13696        "Should have navigated to definition from the GetDefinition response"
13697    );
13698    cx.assert_editor_state(
13699        &r#"fn one() {
13700            let mut a = two();
13701        }
13702
13703        fn «twoˇ»() {}"#
13704            .unindent(),
13705    );
13706
13707    let editors = cx.update_workspace(|workspace, cx| {
13708        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13709    });
13710    cx.update_editor(|_, test_editor_cx| {
13711        assert_eq!(
13712            editors.len(),
13713            1,
13714            "Initially, only one, test, editor should be open in the workspace"
13715        );
13716        assert_eq!(
13717            test_editor_cx.view(),
13718            editors.last().expect("Asserted len is 1")
13719        );
13720    });
13721
13722    set_up_lsp_handlers(true, &mut cx);
13723    let navigated = cx
13724        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13725        .await
13726        .expect("Failed to navigate to lookup references");
13727    assert_eq!(
13728        navigated,
13729        Navigated::Yes,
13730        "Should have navigated to references as a fallback after empty GoToDefinition response"
13731    );
13732    // We should not change the selections in the existing file,
13733    // if opening another milti buffer with the references
13734    cx.assert_editor_state(
13735        &r#"fn one() {
13736            let mut a = two();
13737        }
13738
13739        fn «twoˇ»() {}"#
13740            .unindent(),
13741    );
13742    let editors = cx.update_workspace(|workspace, cx| {
13743        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13744    });
13745    cx.update_editor(|_, test_editor_cx| {
13746        assert_eq!(
13747            editors.len(),
13748            2,
13749            "After falling back to references search, we open a new editor with the results"
13750        );
13751        let references_fallback_text = editors
13752            .into_iter()
13753            .find(|new_editor| new_editor != test_editor_cx.view())
13754            .expect("Should have one non-test editor now")
13755            .read(test_editor_cx)
13756            .text(test_editor_cx);
13757        assert_eq!(
13758            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13759            "Should use the range from the references response and not the GoToDefinition one"
13760        );
13761    });
13762}
13763
13764#[gpui::test]
13765async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13766    init_test(cx, |_| {});
13767
13768    let language = Arc::new(Language::new(
13769        LanguageConfig::default(),
13770        Some(tree_sitter_rust::LANGUAGE.into()),
13771    ));
13772
13773    let text = r#"
13774        #[cfg(test)]
13775        mod tests() {
13776            #[test]
13777            fn runnable_1() {
13778                let a = 1;
13779            }
13780
13781            #[test]
13782            fn runnable_2() {
13783                let a = 1;
13784                let b = 2;
13785            }
13786        }
13787    "#
13788    .unindent();
13789
13790    let fs = FakeFs::new(cx.executor());
13791    fs.insert_file("/file.rs", Default::default()).await;
13792
13793    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13794    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13795    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13796    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13797    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13798
13799    let editor = cx.new_view(|cx| {
13800        Editor::new(
13801            EditorMode::Full,
13802            multi_buffer,
13803            Some(project.clone()),
13804            true,
13805            cx,
13806        )
13807    });
13808
13809    editor.update(cx, |editor, cx| {
13810        editor.tasks.insert(
13811            (buffer.read(cx).remote_id(), 3),
13812            RunnableTasks {
13813                templates: vec![],
13814                offset: MultiBufferOffset(43),
13815                column: 0,
13816                extra_variables: HashMap::default(),
13817                context_range: BufferOffset(43)..BufferOffset(85),
13818            },
13819        );
13820        editor.tasks.insert(
13821            (buffer.read(cx).remote_id(), 8),
13822            RunnableTasks {
13823                templates: vec![],
13824                offset: MultiBufferOffset(86),
13825                column: 0,
13826                extra_variables: HashMap::default(),
13827                context_range: BufferOffset(86)..BufferOffset(191),
13828            },
13829        );
13830
13831        // Test finding task when cursor is inside function body
13832        editor.change_selections(None, cx, |s| {
13833            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13834        });
13835        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13836        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13837
13838        // Test finding task when cursor is on function name
13839        editor.change_selections(None, cx, |s| {
13840            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13841        });
13842        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13843        assert_eq!(row, 8, "Should find task when cursor is on function name");
13844    });
13845}
13846
13847fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13848    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13849    point..point
13850}
13851
13852fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13853    let (text, ranges) = marked_text_ranges(marked_text, true);
13854    assert_eq!(view.text(cx), text);
13855    assert_eq!(
13856        view.selections.ranges(cx),
13857        ranges,
13858        "Assert selections are {}",
13859        marked_text
13860    );
13861}
13862
13863pub fn handle_signature_help_request(
13864    cx: &mut EditorLspTestContext,
13865    mocked_response: lsp::SignatureHelp,
13866) -> impl Future<Output = ()> {
13867    let mut request =
13868        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13869            let mocked_response = mocked_response.clone();
13870            async move { Ok(Some(mocked_response)) }
13871        });
13872
13873    async move {
13874        request.next().await;
13875    }
13876}
13877
13878/// Handle completion request passing a marked string specifying where the completion
13879/// should be triggered from using '|' character, what range should be replaced, and what completions
13880/// should be returned using '<' and '>' to delimit the range
13881pub fn handle_completion_request(
13882    cx: &mut EditorLspTestContext,
13883    marked_string: &str,
13884    completions: Vec<&'static str>,
13885    counter: Arc<AtomicUsize>,
13886) -> impl Future<Output = ()> {
13887    let complete_from_marker: TextRangeMarker = '|'.into();
13888    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13889    let (_, mut marked_ranges) = marked_text_ranges_by(
13890        marked_string,
13891        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13892    );
13893
13894    let complete_from_position =
13895        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13896    let replace_range =
13897        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13898
13899    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13900        let completions = completions.clone();
13901        counter.fetch_add(1, atomic::Ordering::Release);
13902        async move {
13903            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13904            assert_eq!(
13905                params.text_document_position.position,
13906                complete_from_position
13907            );
13908            Ok(Some(lsp::CompletionResponse::Array(
13909                completions
13910                    .iter()
13911                    .map(|completion_text| lsp::CompletionItem {
13912                        label: completion_text.to_string(),
13913                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13914                            range: replace_range,
13915                            new_text: completion_text.to_string(),
13916                        })),
13917                        ..Default::default()
13918                    })
13919                    .collect(),
13920            )))
13921        }
13922    });
13923
13924    async move {
13925        request.next().await;
13926    }
13927}
13928
13929fn handle_resolve_completion_request(
13930    cx: &mut EditorLspTestContext,
13931    edits: Option<Vec<(&'static str, &'static str)>>,
13932) -> impl Future<Output = ()> {
13933    let edits = edits.map(|edits| {
13934        edits
13935            .iter()
13936            .map(|(marked_string, new_text)| {
13937                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13938                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13939                lsp::TextEdit::new(replace_range, new_text.to_string())
13940            })
13941            .collect::<Vec<_>>()
13942    });
13943
13944    let mut request =
13945        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13946            let edits = edits.clone();
13947            async move {
13948                Ok(lsp::CompletionItem {
13949                    additional_text_edits: edits,
13950                    ..Default::default()
13951                })
13952            }
13953        });
13954
13955    async move {
13956        request.next().await;
13957    }
13958}
13959
13960pub(crate) fn update_test_language_settings(
13961    cx: &mut TestAppContext,
13962    f: impl Fn(&mut AllLanguageSettingsContent),
13963) {
13964    cx.update(|cx| {
13965        SettingsStore::update_global(cx, |store, cx| {
13966            store.update_user_settings::<AllLanguageSettings>(cx, f);
13967        });
13968    });
13969}
13970
13971pub(crate) fn update_test_project_settings(
13972    cx: &mut TestAppContext,
13973    f: impl Fn(&mut ProjectSettings),
13974) {
13975    cx.update(|cx| {
13976        SettingsStore::update_global(cx, |store, cx| {
13977            store.update_user_settings::<ProjectSettings>(cx, f);
13978        });
13979    });
13980}
13981
13982pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13983    cx.update(|cx| {
13984        assets::Assets.load_test_fonts(cx);
13985        let store = SettingsStore::test(cx);
13986        cx.set_global(store);
13987        theme::init(theme::LoadThemes::JustBase, cx);
13988        release_channel::init(SemanticVersion::default(), cx);
13989        client::init_settings(cx);
13990        language::init(cx);
13991        Project::init_settings(cx);
13992        workspace::init_settings(cx);
13993        crate::init(cx);
13994    });
13995
13996    update_test_language_settings(cx, f);
13997}
13998
13999#[track_caller]
14000fn assert_hunk_revert(
14001    not_reverted_text_with_selections: &str,
14002    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14003    expected_reverted_text_with_selections: &str,
14004    base_text: &str,
14005    cx: &mut EditorLspTestContext,
14006) {
14007    cx.set_state(not_reverted_text_with_selections);
14008    cx.update_editor(|editor, cx| {
14009        editor
14010            .buffer()
14011            .read(cx)
14012            .as_singleton()
14013            .unwrap()
14014            .update(cx, |buffer, cx| {
14015                buffer.set_diff_base(Some(base_text.into()), cx);
14016            });
14017    });
14018    cx.executor().run_until_parked();
14019
14020    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14021        let snapshot = editor.buffer().read(cx).snapshot(cx);
14022        let reverted_hunk_statuses = snapshot
14023            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
14024            .map(|hunk| hunk_status(&hunk))
14025            .collect::<Vec<_>>();
14026
14027        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14028        reverted_hunk_statuses
14029    });
14030    cx.executor().run_until_parked();
14031    cx.assert_editor_state(expected_reverted_text_with_selections);
14032    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14033}