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::AtomicUsize;
   35use std::sync::atomic::{self, AtomicBool};
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use test::editor_lsp_test_context::rust_lang;
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let group_interval = Duration::from_millis(1);
  174    let buffer = cx.new_model(|cx| {
  175        let mut buf = language::Buffer::local("123456", cx);
  176        buf.set_group_interval(group_interval);
  177        buf
  178    });
  179    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  180    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  181
  182    _ = editor.update(cx, |editor, cx| {
  183        editor.start_transaction_at(now, cx);
  184        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  185
  186        editor.insert("cd", cx);
  187        editor.end_transaction_at(now, cx);
  188        assert_eq!(editor.text(cx), "12cd56");
  189        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  190
  191        editor.start_transaction_at(now, cx);
  192        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  193        editor.insert("e", cx);
  194        editor.end_transaction_at(now, cx);
  195        assert_eq!(editor.text(cx), "12cde6");
  196        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  197
  198        now += group_interval + Duration::from_millis(1);
  199        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  200
  201        // Simulate an edit in another editor
  202        buffer.update(cx, |buffer, cx| {
  203            buffer.start_transaction_at(now, cx);
  204            buffer.edit([(0..1, "a")], None, cx);
  205            buffer.edit([(1..1, "b")], None, cx);
  206            buffer.end_transaction_at(now, cx);
  207        });
  208
  209        assert_eq!(editor.text(cx), "ab2cde6");
  210        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  211
  212        // Last transaction happened past the group interval in a different editor.
  213        // Undo it individually and don't restore selections.
  214        editor.undo(&Undo, cx);
  215        assert_eq!(editor.text(cx), "12cde6");
  216        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  217
  218        // First two transactions happened within the group interval in this editor.
  219        // Undo them together and restore selections.
  220        editor.undo(&Undo, cx);
  221        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  222        assert_eq!(editor.text(cx), "123456");
  223        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  224
  225        // Redo the first two transactions together.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "12cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  229
  230        // Redo the last transaction on its own.
  231        editor.redo(&Redo, cx);
  232        assert_eq!(editor.text(cx), "ab2cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  234
  235        // Test empty transactions.
  236        editor.start_transaction_at(now, cx);
  237        editor.end_transaction_at(now, cx);
  238        editor.undo(&Undo, cx);
  239        assert_eq!(editor.text(cx), "12cde6");
  240    });
  241}
  242
  243#[gpui::test]
  244fn test_ime_composition(cx: &mut TestAppContext) {
  245    init_test(cx, |_| {});
  246
  247    let buffer = cx.new_model(|cx| {
  248        let mut buffer = language::Buffer::local("abcde", cx);
  249        // Ensure automatic grouping doesn't occur.
  250        buffer.set_group_interval(Duration::ZERO);
  251        buffer
  252    });
  253
  254    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  255    cx.add_window(|cx| {
  256        let mut editor = build_editor(buffer.clone(), cx);
  257
  258        // Start a new IME composition.
  259        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  260        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  261        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  262        assert_eq!(editor.text(cx), "äbcde");
  263        assert_eq!(
  264            editor.marked_text_ranges(cx),
  265            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  266        );
  267
  268        // Finalize IME composition.
  269        editor.replace_text_in_range(None, "ā", cx);
  270        assert_eq!(editor.text(cx), "ābcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272
  273        // IME composition edits are grouped and are undone/redone at once.
  274        editor.undo(&Default::default(), cx);
  275        assert_eq!(editor.text(cx), "abcde");
  276        assert_eq!(editor.marked_text_ranges(cx), None);
  277        editor.redo(&Default::default(), cx);
  278        assert_eq!(editor.text(cx), "ābcde");
  279        assert_eq!(editor.marked_text_ranges(cx), None);
  280
  281        // Start a new IME composition.
  282        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  283        assert_eq!(
  284            editor.marked_text_ranges(cx),
  285            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  286        );
  287
  288        // Undoing during an IME composition cancels it.
  289        editor.undo(&Default::default(), cx);
  290        assert_eq!(editor.text(cx), "ābcde");
  291        assert_eq!(editor.marked_text_ranges(cx), None);
  292
  293        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  294        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  295        assert_eq!(editor.text(cx), "ābcdè");
  296        assert_eq!(
  297            editor.marked_text_ranges(cx),
  298            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  299        );
  300
  301        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  302        editor.replace_text_in_range(Some(4..999), "ę", cx);
  303        assert_eq!(editor.text(cx), "ābcdę");
  304        assert_eq!(editor.marked_text_ranges(cx), None);
  305
  306        // Start a new IME composition with multiple cursors.
  307        editor.change_selections(None, cx, |s| {
  308            s.select_ranges([
  309                OffsetUtf16(1)..OffsetUtf16(1),
  310                OffsetUtf16(3)..OffsetUtf16(3),
  311                OffsetUtf16(5)..OffsetUtf16(5),
  312            ])
  313        });
  314        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  315        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  316        assert_eq!(
  317            editor.marked_text_ranges(cx),
  318            Some(vec![
  319                OffsetUtf16(0)..OffsetUtf16(3),
  320                OffsetUtf16(4)..OffsetUtf16(7),
  321                OffsetUtf16(8)..OffsetUtf16(11)
  322            ])
  323        );
  324
  325        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  326        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  327        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  328        assert_eq!(
  329            editor.marked_text_ranges(cx),
  330            Some(vec![
  331                OffsetUtf16(1)..OffsetUtf16(2),
  332                OffsetUtf16(5)..OffsetUtf16(6),
  333                OffsetUtf16(9)..OffsetUtf16(10)
  334            ])
  335        );
  336
  337        // Finalize IME composition with multiple cursors.
  338        editor.replace_text_in_range(Some(9..10), "2", cx);
  339        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  340        assert_eq!(editor.marked_text_ranges(cx), None);
  341
  342        editor
  343    });
  344}
  345
  346#[gpui::test]
  347fn test_selection_with_mouse(cx: &mut TestAppContext) {
  348    init_test(cx, |_| {});
  349
  350    let editor = cx.add_window(|cx| {
  351        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  352        build_editor(buffer, cx)
  353    });
  354
  355    _ = editor.update(cx, |view, cx| {
  356        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  357    });
  358    assert_eq!(
  359        editor
  360            .update(cx, |view, cx| view.selections.display_ranges(cx))
  361            .unwrap(),
  362        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  363    );
  364
  365    _ = editor.update(cx, |view, cx| {
  366        view.update_selection(
  367            DisplayPoint::new(DisplayRow(3), 3),
  368            0,
  369            gpui::Point::<f32>::default(),
  370            cx,
  371        );
  372    });
  373
  374    assert_eq!(
  375        editor
  376            .update(cx, |view, cx| view.selections.display_ranges(cx))
  377            .unwrap(),
  378        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  379    );
  380
  381    _ = editor.update(cx, |view, cx| {
  382        view.update_selection(
  383            DisplayPoint::new(DisplayRow(1), 1),
  384            0,
  385            gpui::Point::<f32>::default(),
  386            cx,
  387        );
  388    });
  389
  390    assert_eq!(
  391        editor
  392            .update(cx, |view, cx| view.selections.display_ranges(cx))
  393            .unwrap(),
  394        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  395    );
  396
  397    _ = editor.update(cx, |view, cx| {
  398        view.end_selection(cx);
  399        view.update_selection(
  400            DisplayPoint::new(DisplayRow(3), 3),
  401            0,
  402            gpui::Point::<f32>::default(),
  403            cx,
  404        );
  405    });
  406
  407    assert_eq!(
  408        editor
  409            .update(cx, |view, cx| view.selections.display_ranges(cx))
  410            .unwrap(),
  411        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  412    );
  413
  414    _ = editor.update(cx, |view, cx| {
  415        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  416        view.update_selection(
  417            DisplayPoint::new(DisplayRow(0), 0),
  418            0,
  419            gpui::Point::<f32>::default(),
  420            cx,
  421        );
  422    });
  423
  424    assert_eq!(
  425        editor
  426            .update(cx, |view, cx| view.selections.display_ranges(cx))
  427            .unwrap(),
  428        [
  429            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  430            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  431        ]
  432    );
  433
  434    _ = editor.update(cx, |view, cx| {
  435        view.end_selection(cx);
  436    });
  437
  438    assert_eq!(
  439        editor
  440            .update(cx, |view, cx| view.selections.display_ranges(cx))
  441            .unwrap(),
  442        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  443    );
  444}
  445
  446#[gpui::test]
  447fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  448    init_test(cx, |_| {});
  449
  450    let editor = cx.add_window(|cx| {
  451        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  452        build_editor(buffer, cx)
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.end_selection(cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  465    });
  466
  467    _ = editor.update(cx, |view, cx| {
  468        view.end_selection(cx);
  469    });
  470
  471    assert_eq!(
  472        editor
  473            .update(cx, |view, cx| view.selections.display_ranges(cx))
  474            .unwrap(),
  475        [
  476            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  477            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  478        ]
  479    );
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  483    });
  484
  485    _ = editor.update(cx, |view, cx| {
  486        view.end_selection(cx);
  487    });
  488
  489    assert_eq!(
  490        editor
  491            .update(cx, |view, cx| view.selections.display_ranges(cx))
  492            .unwrap(),
  493        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  494    );
  495}
  496
  497#[gpui::test]
  498fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  499    init_test(cx, |_| {});
  500
  501    let view = cx.add_window(|cx| {
  502        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  503        build_editor(buffer, cx)
  504    });
  505
  506    _ = view.update(cx, |view, cx| {
  507        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  508        assert_eq!(
  509            view.selections.display_ranges(cx),
  510            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  511        );
  512    });
  513
  514    _ = view.update(cx, |view, cx| {
  515        view.update_selection(
  516            DisplayPoint::new(DisplayRow(3), 3),
  517            0,
  518            gpui::Point::<f32>::default(),
  519            cx,
  520        );
  521        assert_eq!(
  522            view.selections.display_ranges(cx),
  523            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  524        );
  525    });
  526
  527    _ = view.update(cx, |view, cx| {
  528        view.cancel(&Cancel, cx);
  529        view.update_selection(
  530            DisplayPoint::new(DisplayRow(1), 1),
  531            0,
  532            gpui::Point::<f32>::default(),
  533            cx,
  534        );
  535        assert_eq!(
  536            view.selections.display_ranges(cx),
  537            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  538        );
  539    });
  540}
  541
  542#[gpui::test]
  543fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  544    init_test(cx, |_| {});
  545
  546    let view = cx.add_window(|cx| {
  547        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  548        build_editor(buffer, cx)
  549    });
  550
  551    _ = view.update(cx, |view, cx| {
  552        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  553        assert_eq!(
  554            view.selections.display_ranges(cx),
  555            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  556        );
  557
  558        view.move_down(&Default::default(), cx);
  559        assert_eq!(
  560            view.selections.display_ranges(cx),
  561            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  562        );
  563
  564        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  565        assert_eq!(
  566            view.selections.display_ranges(cx),
  567            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  568        );
  569
  570        view.move_up(&Default::default(), cx);
  571        assert_eq!(
  572            view.selections.display_ranges(cx),
  573            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  574        );
  575    });
  576}
  577
  578#[gpui::test]
  579fn test_clone(cx: &mut TestAppContext) {
  580    init_test(cx, |_| {});
  581
  582    let (text, selection_ranges) = marked_text_ranges(
  583        indoc! {"
  584            one
  585            two
  586            threeˇ
  587            four
  588            fiveˇ
  589        "},
  590        true,
  591    );
  592
  593    let editor = cx.add_window(|cx| {
  594        let buffer = MultiBuffer::build_simple(&text, cx);
  595        build_editor(buffer, cx)
  596    });
  597
  598    _ = editor.update(cx, |editor, cx| {
  599        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  600        editor.fold_creases(
  601            vec![
  602                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  603                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  604            ],
  605            true,
  606            cx,
  607        );
  608    });
  609
  610    let cloned_editor = editor
  611        .update(cx, |editor, cx| {
  612            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  613        })
  614        .unwrap()
  615        .unwrap();
  616
  617    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  618    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  619
  620    assert_eq!(
  621        cloned_editor
  622            .update(cx, |e, cx| e.display_text(cx))
  623            .unwrap(),
  624        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  625    );
  626    assert_eq!(
  627        cloned_snapshot
  628            .folds_in_range(0..text.len())
  629            .collect::<Vec<_>>(),
  630        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  631    );
  632    assert_set_eq!(
  633        cloned_editor
  634            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  635            .unwrap(),
  636        editor
  637            .update(cx, |editor, cx| editor.selections.ranges(cx))
  638            .unwrap()
  639    );
  640    assert_set_eq!(
  641        cloned_editor
  642            .update(cx, |e, cx| e.selections.display_ranges(cx))
  643            .unwrap(),
  644        editor
  645            .update(cx, |e, cx| e.selections.display_ranges(cx))
  646            .unwrap()
  647    );
  648}
  649
  650#[gpui::test]
  651async fn test_navigation_history(cx: &mut TestAppContext) {
  652    init_test(cx, |_| {});
  653
  654    use workspace::item::Item;
  655
  656    let fs = FakeFs::new(cx.executor());
  657    let project = Project::test(fs, [], cx).await;
  658    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  659    let pane = workspace
  660        .update(cx, |workspace, _| workspace.active_pane().clone())
  661        .unwrap();
  662
  663    _ = workspace.update(cx, |_v, cx| {
  664        cx.new_view(|cx| {
  665            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  666            let mut editor = build_editor(buffer.clone(), cx);
  667            let handle = cx.view();
  668            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  669
  670            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  671                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  672            }
  673
  674            // Move the cursor a small distance.
  675            // Nothing is added to the navigation history.
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  679                ])
  680            });
  681            editor.change_selections(None, cx, |s| {
  682                s.select_display_ranges([
  683                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  684                ])
  685            });
  686            assert!(pop_history(&mut editor, cx).is_none());
  687
  688            // Move the cursor a large distance.
  689            // The history can jump back to the previous position.
  690            editor.change_selections(None, cx, |s| {
  691                s.select_display_ranges([
  692                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  693                ])
  694            });
  695            let nav_entry = pop_history(&mut editor, cx).unwrap();
  696            editor.navigate(nav_entry.data.unwrap(), cx);
  697            assert_eq!(nav_entry.item.id(), cx.entity_id());
  698            assert_eq!(
  699                editor.selections.display_ranges(cx),
  700                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  701            );
  702            assert!(pop_history(&mut editor, cx).is_none());
  703
  704            // Move the cursor a small distance via the mouse.
  705            // Nothing is added to the navigation history.
  706            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  707            editor.end_selection(cx);
  708            assert_eq!(
  709                editor.selections.display_ranges(cx),
  710                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  711            );
  712            assert!(pop_history(&mut editor, cx).is_none());
  713
  714            // Move the cursor a large distance via the mouse.
  715            // The history can jump back to the previous position.
  716            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  717            editor.end_selection(cx);
  718            assert_eq!(
  719                editor.selections.display_ranges(cx),
  720                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  721            );
  722            let nav_entry = pop_history(&mut editor, cx).unwrap();
  723            editor.navigate(nav_entry.data.unwrap(), cx);
  724            assert_eq!(nav_entry.item.id(), cx.entity_id());
  725            assert_eq!(
  726                editor.selections.display_ranges(cx),
  727                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  728            );
  729            assert!(pop_history(&mut editor, cx).is_none());
  730
  731            // Set scroll position to check later
  732            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  733            let original_scroll_position = editor.scroll_manager.anchor();
  734
  735            // Jump to the end of the document and adjust scroll
  736            editor.move_to_end(&MoveToEnd, cx);
  737            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  738            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            let nav_entry = pop_history(&mut editor, cx).unwrap();
  741            editor.navigate(nav_entry.data.unwrap(), cx);
  742            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  743
  744            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  745            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  746            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  747            let invalid_point = Point::new(9999, 0);
  748            editor.navigate(
  749                Box::new(NavigationData {
  750                    cursor_anchor: invalid_anchor,
  751                    cursor_position: invalid_point,
  752                    scroll_anchor: ScrollAnchor {
  753                        anchor: invalid_anchor,
  754                        offset: Default::default(),
  755                    },
  756                    scroll_top_row: invalid_point.row,
  757                }),
  758                cx,
  759            );
  760            assert_eq!(
  761                editor.selections.display_ranges(cx),
  762                &[editor.max_point(cx)..editor.max_point(cx)]
  763            );
  764            assert_eq!(
  765                editor.scroll_position(cx),
  766                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  767            );
  768
  769            editor
  770        })
  771    });
  772}
  773
  774#[gpui::test]
  775fn test_cancel(cx: &mut TestAppContext) {
  776    init_test(cx, |_| {});
  777
  778    let view = cx.add_window(|cx| {
  779        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  780        build_editor(buffer, cx)
  781    });
  782
  783    _ = view.update(cx, |view, cx| {
  784        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  785        view.update_selection(
  786            DisplayPoint::new(DisplayRow(1), 1),
  787            0,
  788            gpui::Point::<f32>::default(),
  789            cx,
  790        );
  791        view.end_selection(cx);
  792
  793        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  794        view.update_selection(
  795            DisplayPoint::new(DisplayRow(0), 3),
  796            0,
  797            gpui::Point::<f32>::default(),
  798            cx,
  799        );
  800        view.end_selection(cx);
  801        assert_eq!(
  802            view.selections.display_ranges(cx),
  803            [
  804                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  805                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  806            ]
  807        );
  808    });
  809
  810    _ = view.update(cx, |view, cx| {
  811        view.cancel(&Cancel, cx);
  812        assert_eq!(
  813            view.selections.display_ranges(cx),
  814            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  815        );
  816    });
  817
  818    _ = view.update(cx, |view, cx| {
  819        view.cancel(&Cancel, cx);
  820        assert_eq!(
  821            view.selections.display_ranges(cx),
  822            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  823        );
  824    });
  825}
  826
  827#[gpui::test]
  828fn test_fold_action(cx: &mut TestAppContext) {
  829    init_test(cx, |_| {});
  830
  831    let view = cx.add_window(|cx| {
  832        let buffer = MultiBuffer::build_simple(
  833            &"
  834                impl Foo {
  835                    // Hello!
  836
  837                    fn a() {
  838                        1
  839                    }
  840
  841                    fn b() {
  842                        2
  843                    }
  844
  845                    fn c() {
  846                        3
  847                    }
  848                }
  849            "
  850            .unindent(),
  851            cx,
  852        );
  853        build_editor(buffer.clone(), cx)
  854    });
  855
  856    _ = view.update(cx, |view, cx| {
  857        view.change_selections(None, cx, |s| {
  858            s.select_display_ranges([
  859                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  860            ]);
  861        });
  862        view.fold(&Fold, cx);
  863        assert_eq!(
  864            view.display_text(cx),
  865            "
  866                impl Foo {
  867                    // Hello!
  868
  869                    fn a() {
  870                        1
  871                    }
  872
  873                    fn b() {⋯
  874                    }
  875
  876                    fn c() {⋯
  877                    }
  878                }
  879            "
  880            .unindent(),
  881        );
  882
  883        view.fold(&Fold, cx);
  884        assert_eq!(
  885            view.display_text(cx),
  886            "
  887                impl Foo {⋯
  888                }
  889            "
  890            .unindent(),
  891        );
  892
  893        view.unfold_lines(&UnfoldLines, cx);
  894        assert_eq!(
  895            view.display_text(cx),
  896            "
  897                impl Foo {
  898                    // Hello!
  899
  900                    fn a() {
  901                        1
  902                    }
  903
  904                    fn b() {⋯
  905                    }
  906
  907                    fn c() {⋯
  908                    }
  909                }
  910            "
  911            .unindent(),
  912        );
  913
  914        view.unfold_lines(&UnfoldLines, cx);
  915        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  916    });
  917}
  918
  919#[gpui::test]
  920fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  921    init_test(cx, |_| {});
  922
  923    let view = cx.add_window(|cx| {
  924        let buffer = MultiBuffer::build_simple(
  925            &"
  926                class Foo:
  927                    # Hello!
  928
  929                    def a():
  930                        print(1)
  931
  932                    def b():
  933                        print(2)
  934
  935                    def c():
  936                        print(3)
  937            "
  938            .unindent(),
  939            cx,
  940        );
  941        build_editor(buffer.clone(), cx)
  942    });
  943
  944    _ = view.update(cx, |view, cx| {
  945        view.change_selections(None, cx, |s| {
  946            s.select_display_ranges([
  947                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  948            ]);
  949        });
  950        view.fold(&Fold, cx);
  951        assert_eq!(
  952            view.display_text(cx),
  953            "
  954                class Foo:
  955                    # Hello!
  956
  957                    def a():
  958                        print(1)
  959
  960                    def b():⋯
  961
  962                    def c():⋯
  963            "
  964            .unindent(),
  965        );
  966
  967        view.fold(&Fold, cx);
  968        assert_eq!(
  969            view.display_text(cx),
  970            "
  971                class Foo:⋯
  972            "
  973            .unindent(),
  974        );
  975
  976        view.unfold_lines(&UnfoldLines, cx);
  977        assert_eq!(
  978            view.display_text(cx),
  979            "
  980                class Foo:
  981                    # Hello!
  982
  983                    def a():
  984                        print(1)
  985
  986                    def b():⋯
  987
  988                    def c():⋯
  989            "
  990            .unindent(),
  991        );
  992
  993        view.unfold_lines(&UnfoldLines, cx);
  994        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  995    });
  996}
  997
  998#[gpui::test]
  999fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1000    init_test(cx, |_| {});
 1001
 1002    let view = cx.add_window(|cx| {
 1003        let buffer = MultiBuffer::build_simple(
 1004            &"
 1005                class Foo:
 1006                    # Hello!
 1007
 1008                    def a():
 1009                        print(1)
 1010
 1011                    def b():
 1012                        print(2)
 1013
 1014
 1015                    def c():
 1016                        print(3)
 1017
 1018
 1019            "
 1020            .unindent(),
 1021            cx,
 1022        );
 1023        build_editor(buffer.clone(), cx)
 1024    });
 1025
 1026    _ = view.update(cx, |view, cx| {
 1027        view.change_selections(None, cx, |s| {
 1028            s.select_display_ranges([
 1029                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1030            ]);
 1031        });
 1032        view.fold(&Fold, cx);
 1033        assert_eq!(
 1034            view.display_text(cx),
 1035            "
 1036                class Foo:
 1037                    # Hello!
 1038
 1039                    def a():
 1040                        print(1)
 1041
 1042                    def b():⋯
 1043
 1044
 1045                    def c():⋯
 1046
 1047
 1048            "
 1049            .unindent(),
 1050        );
 1051
 1052        view.fold(&Fold, cx);
 1053        assert_eq!(
 1054            view.display_text(cx),
 1055            "
 1056                class Foo:⋯
 1057
 1058
 1059            "
 1060            .unindent(),
 1061        );
 1062
 1063        view.unfold_lines(&UnfoldLines, cx);
 1064        assert_eq!(
 1065            view.display_text(cx),
 1066            "
 1067                class Foo:
 1068                    # Hello!
 1069
 1070                    def a():
 1071                        print(1)
 1072
 1073                    def b():⋯
 1074
 1075
 1076                    def c():⋯
 1077
 1078
 1079            "
 1080            .unindent(),
 1081        );
 1082
 1083        view.unfold_lines(&UnfoldLines, cx);
 1084        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1085    });
 1086}
 1087
 1088#[gpui::test]
 1089fn test_fold_at_level(cx: &mut TestAppContext) {
 1090    init_test(cx, |_| {});
 1091
 1092    let view = cx.add_window(|cx| {
 1093        let buffer = MultiBuffer::build_simple(
 1094            &"
 1095                class Foo:
 1096                    # Hello!
 1097
 1098                    def a():
 1099                        print(1)
 1100
 1101                    def b():
 1102                        print(2)
 1103
 1104
 1105                class Bar:
 1106                    # World!
 1107
 1108                    def a():
 1109                        print(1)
 1110
 1111                    def b():
 1112                        print(2)
 1113
 1114
 1115            "
 1116            .unindent(),
 1117            cx,
 1118        );
 1119        build_editor(buffer.clone(), cx)
 1120    });
 1121
 1122    _ = view.update(cx, |view, cx| {
 1123        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1124        assert_eq!(
 1125            view.display_text(cx),
 1126            "
 1127                class Foo:
 1128                    # Hello!
 1129
 1130                    def a():⋯
 1131
 1132                    def b():⋯
 1133
 1134
 1135                class Bar:
 1136                    # World!
 1137
 1138                    def a():⋯
 1139
 1140                    def b():⋯
 1141
 1142
 1143            "
 1144            .unindent(),
 1145        );
 1146
 1147        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1148        assert_eq!(
 1149            view.display_text(cx),
 1150            "
 1151                class Foo:⋯
 1152
 1153
 1154                class Bar:⋯
 1155
 1156
 1157            "
 1158            .unindent(),
 1159        );
 1160
 1161        view.unfold_all(&UnfoldAll, cx);
 1162        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1163        assert_eq!(
 1164            view.display_text(cx),
 1165            "
 1166                class Foo:
 1167                    # Hello!
 1168
 1169                    def a():
 1170                        print(1)
 1171
 1172                    def b():
 1173                        print(2)
 1174
 1175
 1176                class Bar:
 1177                    # World!
 1178
 1179                    def a():
 1180                        print(1)
 1181
 1182                    def b():
 1183                        print(2)
 1184
 1185
 1186            "
 1187            .unindent(),
 1188        );
 1189
 1190        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1191    });
 1192}
 1193
 1194#[gpui::test]
 1195fn test_move_cursor(cx: &mut TestAppContext) {
 1196    init_test(cx, |_| {});
 1197
 1198    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1199    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1200
 1201    buffer.update(cx, |buffer, cx| {
 1202        buffer.edit(
 1203            vec![
 1204                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1205                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1206            ],
 1207            None,
 1208            cx,
 1209        );
 1210    });
 1211    _ = view.update(cx, |view, cx| {
 1212        assert_eq!(
 1213            view.selections.display_ranges(cx),
 1214            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1215        );
 1216
 1217        view.move_down(&MoveDown, cx);
 1218        assert_eq!(
 1219            view.selections.display_ranges(cx),
 1220            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1221        );
 1222
 1223        view.move_right(&MoveRight, cx);
 1224        assert_eq!(
 1225            view.selections.display_ranges(cx),
 1226            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1227        );
 1228
 1229        view.move_left(&MoveLeft, cx);
 1230        assert_eq!(
 1231            view.selections.display_ranges(cx),
 1232            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1233        );
 1234
 1235        view.move_up(&MoveUp, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1239        );
 1240
 1241        view.move_to_end(&MoveToEnd, cx);
 1242        assert_eq!(
 1243            view.selections.display_ranges(cx),
 1244            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1245        );
 1246
 1247        view.move_to_beginning(&MoveToBeginning, cx);
 1248        assert_eq!(
 1249            view.selections.display_ranges(cx),
 1250            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1251        );
 1252
 1253        view.change_selections(None, cx, |s| {
 1254            s.select_display_ranges([
 1255                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1256            ]);
 1257        });
 1258        view.select_to_beginning(&SelectToBeginning, cx);
 1259        assert_eq!(
 1260            view.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        view.select_to_end(&SelectToEnd, cx);
 1265        assert_eq!(
 1266            view.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1268        );
 1269    });
 1270}
 1271
 1272// TODO: Re-enable this test
 1273#[cfg(target_os = "macos")]
 1274#[gpui::test]
 1275fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1276    init_test(cx, |_| {});
 1277
 1278    let view = cx.add_window(|cx| {
 1279        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1280        build_editor(buffer.clone(), cx)
 1281    });
 1282
 1283    assert_eq!('ⓐ'.len_utf8(), 3);
 1284    assert_eq!('α'.len_utf8(), 2);
 1285
 1286    _ = view.update(cx, |view, cx| {
 1287        view.fold_creases(
 1288            vec![
 1289                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1290                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1291                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1292            ],
 1293            true,
 1294            cx,
 1295        );
 1296        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1297
 1298        view.move_right(&MoveRight, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(0, "".len())]
 1302        );
 1303        view.move_right(&MoveRight, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(0, "ⓐⓑ".len())]
 1307        );
 1308        view.move_right(&MoveRight, cx);
 1309        assert_eq!(
 1310            view.selections.display_ranges(cx),
 1311            &[empty_range(0, "ⓐⓑ⋯".len())]
 1312        );
 1313
 1314        view.move_down(&MoveDown, cx);
 1315        assert_eq!(
 1316            view.selections.display_ranges(cx),
 1317            &[empty_range(1, "ab⋯e".len())]
 1318        );
 1319        view.move_left(&MoveLeft, cx);
 1320        assert_eq!(
 1321            view.selections.display_ranges(cx),
 1322            &[empty_range(1, "ab⋯".len())]
 1323        );
 1324        view.move_left(&MoveLeft, cx);
 1325        assert_eq!(
 1326            view.selections.display_ranges(cx),
 1327            &[empty_range(1, "ab".len())]
 1328        );
 1329        view.move_left(&MoveLeft, cx);
 1330        assert_eq!(
 1331            view.selections.display_ranges(cx),
 1332            &[empty_range(1, "a".len())]
 1333        );
 1334
 1335        view.move_down(&MoveDown, cx);
 1336        assert_eq!(
 1337            view.selections.display_ranges(cx),
 1338            &[empty_range(2, "α".len())]
 1339        );
 1340        view.move_right(&MoveRight, cx);
 1341        assert_eq!(
 1342            view.selections.display_ranges(cx),
 1343            &[empty_range(2, "αβ".len())]
 1344        );
 1345        view.move_right(&MoveRight, cx);
 1346        assert_eq!(
 1347            view.selections.display_ranges(cx),
 1348            &[empty_range(2, "αβ⋯".len())]
 1349        );
 1350        view.move_right(&MoveRight, cx);
 1351        assert_eq!(
 1352            view.selections.display_ranges(cx),
 1353            &[empty_range(2, "αβ⋯ε".len())]
 1354        );
 1355
 1356        view.move_up(&MoveUp, cx);
 1357        assert_eq!(
 1358            view.selections.display_ranges(cx),
 1359            &[empty_range(1, "ab⋯e".len())]
 1360        );
 1361        view.move_down(&MoveDown, cx);
 1362        assert_eq!(
 1363            view.selections.display_ranges(cx),
 1364            &[empty_range(2, "αβ⋯ε".len())]
 1365        );
 1366        view.move_up(&MoveUp, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯e".len())]
 1370        );
 1371
 1372        view.move_up(&MoveUp, cx);
 1373        assert_eq!(
 1374            view.selections.display_ranges(cx),
 1375            &[empty_range(0, "ⓐⓑ".len())]
 1376        );
 1377        view.move_left(&MoveLeft, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[empty_range(0, "".len())]
 1381        );
 1382        view.move_left(&MoveLeft, cx);
 1383        assert_eq!(
 1384            view.selections.display_ranges(cx),
 1385            &[empty_range(0, "".len())]
 1386        );
 1387    });
 1388}
 1389
 1390#[gpui::test]
 1391fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1392    init_test(cx, |_| {});
 1393
 1394    let view = cx.add_window(|cx| {
 1395        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1396        build_editor(buffer.clone(), cx)
 1397    });
 1398    _ = view.update(cx, |view, cx| {
 1399        view.change_selections(None, cx, |s| {
 1400            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1401        });
 1402
 1403        // moving above start of document should move selection to start of document,
 1404        // but the next move down should still be at the original goal_x
 1405        view.move_up(&MoveUp, cx);
 1406        assert_eq!(
 1407            view.selections.display_ranges(cx),
 1408            &[empty_range(0, "".len())]
 1409        );
 1410
 1411        view.move_down(&MoveDown, cx);
 1412        assert_eq!(
 1413            view.selections.display_ranges(cx),
 1414            &[empty_range(1, "abcd".len())]
 1415        );
 1416
 1417        view.move_down(&MoveDown, cx);
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[empty_range(2, "αβγ".len())]
 1421        );
 1422
 1423        view.move_down(&MoveDown, cx);
 1424        assert_eq!(
 1425            view.selections.display_ranges(cx),
 1426            &[empty_range(3, "abcd".len())]
 1427        );
 1428
 1429        view.move_down(&MoveDown, cx);
 1430        assert_eq!(
 1431            view.selections.display_ranges(cx),
 1432            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1433        );
 1434
 1435        // moving past end of document should not change goal_x
 1436        view.move_down(&MoveDown, cx);
 1437        assert_eq!(
 1438            view.selections.display_ranges(cx),
 1439            &[empty_range(5, "".len())]
 1440        );
 1441
 1442        view.move_down(&MoveDown, cx);
 1443        assert_eq!(
 1444            view.selections.display_ranges(cx),
 1445            &[empty_range(5, "".len())]
 1446        );
 1447
 1448        view.move_up(&MoveUp, cx);
 1449        assert_eq!(
 1450            view.selections.display_ranges(cx),
 1451            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1452        );
 1453
 1454        view.move_up(&MoveUp, cx);
 1455        assert_eq!(
 1456            view.selections.display_ranges(cx),
 1457            &[empty_range(3, "abcd".len())]
 1458        );
 1459
 1460        view.move_up(&MoveUp, cx);
 1461        assert_eq!(
 1462            view.selections.display_ranges(cx),
 1463            &[empty_range(2, "αβγ".len())]
 1464        );
 1465    });
 1466}
 1467
 1468#[gpui::test]
 1469fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1470    init_test(cx, |_| {});
 1471    let move_to_beg = MoveToBeginningOfLine {
 1472        stop_at_soft_wraps: true,
 1473    };
 1474
 1475    let move_to_end = MoveToEndOfLine {
 1476        stop_at_soft_wraps: true,
 1477    };
 1478
 1479    let view = cx.add_window(|cx| {
 1480        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1481        build_editor(buffer, cx)
 1482    });
 1483    _ = view.update(cx, |view, cx| {
 1484        view.change_selections(None, cx, |s| {
 1485            s.select_display_ranges([
 1486                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1487                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1488            ]);
 1489        });
 1490    });
 1491
 1492    _ = view.update(cx, |view, cx| {
 1493        view.move_to_beginning_of_line(&move_to_beg, cx);
 1494        assert_eq!(
 1495            view.selections.display_ranges(cx),
 1496            &[
 1497                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1498                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1499            ]
 1500        );
 1501    });
 1502
 1503    _ = view.update(cx, |view, cx| {
 1504        view.move_to_beginning_of_line(&move_to_beg, cx);
 1505        assert_eq!(
 1506            view.selections.display_ranges(cx),
 1507            &[
 1508                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1509                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1510            ]
 1511        );
 1512    });
 1513
 1514    _ = view.update(cx, |view, cx| {
 1515        view.move_to_beginning_of_line(&move_to_beg, cx);
 1516        assert_eq!(
 1517            view.selections.display_ranges(cx),
 1518            &[
 1519                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1520                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1521            ]
 1522        );
 1523    });
 1524
 1525    _ = view.update(cx, |view, cx| {
 1526        view.move_to_end_of_line(&move_to_end, cx);
 1527        assert_eq!(
 1528            view.selections.display_ranges(cx),
 1529            &[
 1530                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1531                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1532            ]
 1533        );
 1534    });
 1535
 1536    // Moving to the end of line again is a no-op.
 1537    _ = view.update(cx, |view, cx| {
 1538        view.move_to_end_of_line(&move_to_end, cx);
 1539        assert_eq!(
 1540            view.selections.display_ranges(cx),
 1541            &[
 1542                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1543                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1544            ]
 1545        );
 1546    });
 1547
 1548    _ = view.update(cx, |view, cx| {
 1549        view.move_left(&MoveLeft, cx);
 1550        view.select_to_beginning_of_line(
 1551            &SelectToBeginningOfLine {
 1552                stop_at_soft_wraps: true,
 1553            },
 1554            cx,
 1555        );
 1556        assert_eq!(
 1557            view.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = view.update(cx, |view, cx| {
 1566        view.select_to_beginning_of_line(
 1567            &SelectToBeginningOfLine {
 1568                stop_at_soft_wraps: true,
 1569            },
 1570            cx,
 1571        );
 1572        assert_eq!(
 1573            view.selections.display_ranges(cx),
 1574            &[
 1575                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1576                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1577            ]
 1578        );
 1579    });
 1580
 1581    _ = view.update(cx, |view, cx| {
 1582        view.select_to_beginning_of_line(
 1583            &SelectToBeginningOfLine {
 1584                stop_at_soft_wraps: true,
 1585            },
 1586            cx,
 1587        );
 1588        assert_eq!(
 1589            view.selections.display_ranges(cx),
 1590            &[
 1591                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1592                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1593            ]
 1594        );
 1595    });
 1596
 1597    _ = view.update(cx, |view, cx| {
 1598        view.select_to_end_of_line(
 1599            &SelectToEndOfLine {
 1600                stop_at_soft_wraps: true,
 1601            },
 1602            cx,
 1603        );
 1604        assert_eq!(
 1605            view.selections.display_ranges(cx),
 1606            &[
 1607                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1608                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1609            ]
 1610        );
 1611    });
 1612
 1613    _ = view.update(cx, |view, cx| {
 1614        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1615        assert_eq!(view.display_text(cx), "ab\n  de");
 1616        assert_eq!(
 1617            view.selections.display_ranges(cx),
 1618            &[
 1619                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1620                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1621            ]
 1622        );
 1623    });
 1624
 1625    _ = view.update(cx, |view, cx| {
 1626        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1627        assert_eq!(view.display_text(cx), "\n");
 1628        assert_eq!(
 1629            view.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636}
 1637
 1638#[gpui::test]
 1639fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1640    init_test(cx, |_| {});
 1641    let move_to_beg = MoveToBeginningOfLine {
 1642        stop_at_soft_wraps: false,
 1643    };
 1644
 1645    let move_to_end = MoveToEndOfLine {
 1646        stop_at_soft_wraps: false,
 1647    };
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656
 1657        // We expect the following lines after wrapping
 1658        // ```
 1659        // thequickbrownfox
 1660        // jumpedoverthelazydo
 1661        // gs
 1662        // ```
 1663        // The final `gs` was soft-wrapped onto a new line.
 1664        assert_eq!(
 1665            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1666            view.display_text(cx),
 1667        );
 1668
 1669        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1670        // Start the cursor at the `k` on the first line
 1671        view.change_selections(None, cx, |s| {
 1672            s.select_display_ranges([
 1673                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1674            ]);
 1675        });
 1676
 1677        // Moving to the beginning of the line should put us at the beginning of the line.
 1678        view.move_to_beginning_of_line(&move_to_beg, cx);
 1679        assert_eq!(
 1680            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1681            view.selections.display_ranges(cx)
 1682        );
 1683
 1684        // Moving to the end of the line should put us at the end of the line.
 1685        view.move_to_end_of_line(&move_to_end, cx);
 1686        assert_eq!(
 1687            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1688            view.selections.display_ranges(cx)
 1689        );
 1690
 1691        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1692        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1693        view.change_selections(None, cx, |s| {
 1694            s.select_display_ranges([
 1695                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1696            ]);
 1697        });
 1698
 1699        // Moving to the beginning of the line should put us at the start of the second line of
 1700        // display text, i.e., the `j`.
 1701        view.move_to_beginning_of_line(&move_to_beg, cx);
 1702        assert_eq!(
 1703            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1704            view.selections.display_ranges(cx)
 1705        );
 1706
 1707        // Moving to the beginning of the line again should be a no-op.
 1708        view.move_to_beginning_of_line(&move_to_beg, cx);
 1709        assert_eq!(
 1710            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1711            view.selections.display_ranges(cx)
 1712        );
 1713
 1714        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1715        // next display line.
 1716        view.move_to_end_of_line(&move_to_end, cx);
 1717        assert_eq!(
 1718            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1719            view.selections.display_ranges(cx)
 1720        );
 1721
 1722        // Moving to the end of the line again should be a no-op.
 1723        view.move_to_end_of_line(&move_to_end, cx);
 1724        assert_eq!(
 1725            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1726            view.selections.display_ranges(cx)
 1727        );
 1728    });
 1729}
 1730
 1731#[gpui::test]
 1732fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1733    init_test(cx, |_| {});
 1734
 1735    let view = cx.add_window(|cx| {
 1736        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1737        build_editor(buffer, cx)
 1738    });
 1739    _ = view.update(cx, |view, cx| {
 1740        view.change_selections(None, cx, |s| {
 1741            s.select_display_ranges([
 1742                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1743                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1744            ])
 1745        });
 1746
 1747        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1748        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1749
 1750        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1751        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1752
 1753        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1754        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1755
 1756        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1757        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1758
 1759        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1760        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1761
 1762        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1763        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1764
 1765        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1766        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1767
 1768        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1769        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1770
 1771        view.move_right(&MoveRight, cx);
 1772        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1773        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1774
 1775        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1776        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1777
 1778        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1779        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1780    });
 1781}
 1782
 1783#[gpui::test]
 1784fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1785    init_test(cx, |_| {});
 1786
 1787    let view = cx.add_window(|cx| {
 1788        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1789        build_editor(buffer, cx)
 1790    });
 1791
 1792    _ = view.update(cx, |view, cx| {
 1793        view.set_wrap_width(Some(140.0.into()), cx);
 1794        assert_eq!(
 1795            view.display_text(cx),
 1796            "use one::{\n    two::three::\n    four::five\n};"
 1797        );
 1798
 1799        view.change_selections(None, cx, |s| {
 1800            s.select_display_ranges([
 1801                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1802            ]);
 1803        });
 1804
 1805        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1806        assert_eq!(
 1807            view.selections.display_ranges(cx),
 1808            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1809        );
 1810
 1811        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1812        assert_eq!(
 1813            view.selections.display_ranges(cx),
 1814            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1815        );
 1816
 1817        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1818        assert_eq!(
 1819            view.selections.display_ranges(cx),
 1820            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1821        );
 1822
 1823        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1824        assert_eq!(
 1825            view.selections.display_ranges(cx),
 1826            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1827        );
 1828
 1829        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1830        assert_eq!(
 1831            view.selections.display_ranges(cx),
 1832            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1833        );
 1834
 1835        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1836        assert_eq!(
 1837            view.selections.display_ranges(cx),
 1838            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1839        );
 1840    });
 1841}
 1842
 1843#[gpui::test]
 1844async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1845    init_test(cx, |_| {});
 1846    let mut cx = EditorTestContext::new(cx).await;
 1847
 1848    let line_height = cx.editor(|editor, cx| {
 1849        editor
 1850            .style()
 1851            .unwrap()
 1852            .text
 1853            .line_height_in_pixels(cx.rem_size())
 1854    });
 1855    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1856
 1857    cx.set_state(
 1858        &r#"ˇone
 1859        two
 1860
 1861        three
 1862        fourˇ
 1863        five
 1864
 1865        six"#
 1866            .unindent(),
 1867    );
 1868
 1869    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1870    cx.assert_editor_state(
 1871        &r#"one
 1872        two
 1873        ˇ
 1874        three
 1875        four
 1876        five
 1877        ˇ
 1878        six"#
 1879            .unindent(),
 1880    );
 1881
 1882    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1883    cx.assert_editor_state(
 1884        &r#"one
 1885        two
 1886
 1887        three
 1888        four
 1889        five
 1890        ˇ
 1891        sixˇ"#
 1892            .unindent(),
 1893    );
 1894
 1895    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1896    cx.assert_editor_state(
 1897        &r#"one
 1898        two
 1899
 1900        three
 1901        four
 1902        five
 1903
 1904        sixˇ"#
 1905            .unindent(),
 1906    );
 1907
 1908    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1909    cx.assert_editor_state(
 1910        &r#"one
 1911        two
 1912
 1913        three
 1914        four
 1915        five
 1916        ˇ
 1917        six"#
 1918            .unindent(),
 1919    );
 1920
 1921    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1922    cx.assert_editor_state(
 1923        &r#"one
 1924        two
 1925        ˇ
 1926        three
 1927        four
 1928        five
 1929
 1930        six"#
 1931            .unindent(),
 1932    );
 1933
 1934    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1935    cx.assert_editor_state(
 1936        &r#"ˇone
 1937        two
 1938
 1939        three
 1940        four
 1941        five
 1942
 1943        six"#
 1944            .unindent(),
 1945    );
 1946}
 1947
 1948#[gpui::test]
 1949async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1950    init_test(cx, |_| {});
 1951    let mut cx = EditorTestContext::new(cx).await;
 1952    let line_height = cx.editor(|editor, cx| {
 1953        editor
 1954            .style()
 1955            .unwrap()
 1956            .text
 1957            .line_height_in_pixels(cx.rem_size())
 1958    });
 1959    let window = cx.window;
 1960    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1961
 1962    cx.set_state(
 1963        r#"ˇone
 1964        two
 1965        three
 1966        four
 1967        five
 1968        six
 1969        seven
 1970        eight
 1971        nine
 1972        ten
 1973        "#,
 1974    );
 1975
 1976    cx.update_editor(|editor, cx| {
 1977        assert_eq!(
 1978            editor.snapshot(cx).scroll_position(),
 1979            gpui::Point::new(0., 0.)
 1980        );
 1981        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1982        assert_eq!(
 1983            editor.snapshot(cx).scroll_position(),
 1984            gpui::Point::new(0., 3.)
 1985        );
 1986        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1987        assert_eq!(
 1988            editor.snapshot(cx).scroll_position(),
 1989            gpui::Point::new(0., 6.)
 1990        );
 1991        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1992        assert_eq!(
 1993            editor.snapshot(cx).scroll_position(),
 1994            gpui::Point::new(0., 3.)
 1995        );
 1996
 1997        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1998        assert_eq!(
 1999            editor.snapshot(cx).scroll_position(),
 2000            gpui::Point::new(0., 1.)
 2001        );
 2002        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2003        assert_eq!(
 2004            editor.snapshot(cx).scroll_position(),
 2005            gpui::Point::new(0., 3.)
 2006        );
 2007    });
 2008}
 2009
 2010#[gpui::test]
 2011async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2012    init_test(cx, |_| {});
 2013    let mut cx = EditorTestContext::new(cx).await;
 2014
 2015    let line_height = cx.update_editor(|editor, cx| {
 2016        editor.set_vertical_scroll_margin(2, cx);
 2017        editor
 2018            .style()
 2019            .unwrap()
 2020            .text
 2021            .line_height_in_pixels(cx.rem_size())
 2022    });
 2023    let window = cx.window;
 2024    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2025
 2026    cx.set_state(
 2027        r#"ˇone
 2028            two
 2029            three
 2030            four
 2031            five
 2032            six
 2033            seven
 2034            eight
 2035            nine
 2036            ten
 2037        "#,
 2038    );
 2039    cx.update_editor(|editor, cx| {
 2040        assert_eq!(
 2041            editor.snapshot(cx).scroll_position(),
 2042            gpui::Point::new(0., 0.0)
 2043        );
 2044    });
 2045
 2046    // Add a cursor below the visible area. Since both cursors cannot fit
 2047    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2048    // allows the vertical scroll margin below that cursor.
 2049    cx.update_editor(|editor, cx| {
 2050        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2051            selections.select_ranges([
 2052                Point::new(0, 0)..Point::new(0, 0),
 2053                Point::new(6, 0)..Point::new(6, 0),
 2054            ]);
 2055        })
 2056    });
 2057    cx.update_editor(|editor, cx| {
 2058        assert_eq!(
 2059            editor.snapshot(cx).scroll_position(),
 2060            gpui::Point::new(0., 3.0)
 2061        );
 2062    });
 2063
 2064    // Move down. The editor cursor scrolls down to track the newest cursor.
 2065    cx.update_editor(|editor, cx| {
 2066        editor.move_down(&Default::default(), cx);
 2067    });
 2068    cx.update_editor(|editor, cx| {
 2069        assert_eq!(
 2070            editor.snapshot(cx).scroll_position(),
 2071            gpui::Point::new(0., 4.0)
 2072        );
 2073    });
 2074
 2075    // Add a cursor above the visible area. Since both cursors fit on screen,
 2076    // the editor scrolls to show both.
 2077    cx.update_editor(|editor, cx| {
 2078        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2079            selections.select_ranges([
 2080                Point::new(1, 0)..Point::new(1, 0),
 2081                Point::new(6, 0)..Point::new(6, 0),
 2082            ]);
 2083        })
 2084    });
 2085    cx.update_editor(|editor, cx| {
 2086        assert_eq!(
 2087            editor.snapshot(cx).scroll_position(),
 2088            gpui::Point::new(0., 1.0)
 2089        );
 2090    });
 2091}
 2092
 2093#[gpui::test]
 2094async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2095    init_test(cx, |_| {});
 2096    let mut cx = EditorTestContext::new(cx).await;
 2097
 2098    let line_height = cx.editor(|editor, cx| {
 2099        editor
 2100            .style()
 2101            .unwrap()
 2102            .text
 2103            .line_height_in_pixels(cx.rem_size())
 2104    });
 2105    let window = cx.window;
 2106    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2107    cx.set_state(
 2108        &r#"
 2109        ˇone
 2110        two
 2111        threeˇ
 2112        four
 2113        five
 2114        six
 2115        seven
 2116        eight
 2117        nine
 2118        ten
 2119        "#
 2120        .unindent(),
 2121    );
 2122
 2123    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2124    cx.assert_editor_state(
 2125        &r#"
 2126        one
 2127        two
 2128        three
 2129        ˇfour
 2130        five
 2131        sixˇ
 2132        seven
 2133        eight
 2134        nine
 2135        ten
 2136        "#
 2137        .unindent(),
 2138    );
 2139
 2140    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2141    cx.assert_editor_state(
 2142        &r#"
 2143        one
 2144        two
 2145        three
 2146        four
 2147        five
 2148        six
 2149        ˇseven
 2150        eight
 2151        nineˇ
 2152        ten
 2153        "#
 2154        .unindent(),
 2155    );
 2156
 2157    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2158    cx.assert_editor_state(
 2159        &r#"
 2160        one
 2161        two
 2162        three
 2163        ˇfour
 2164        five
 2165        sixˇ
 2166        seven
 2167        eight
 2168        nine
 2169        ten
 2170        "#
 2171        .unindent(),
 2172    );
 2173
 2174    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2175    cx.assert_editor_state(
 2176        &r#"
 2177        ˇone
 2178        two
 2179        threeˇ
 2180        four
 2181        five
 2182        six
 2183        seven
 2184        eight
 2185        nine
 2186        ten
 2187        "#
 2188        .unindent(),
 2189    );
 2190
 2191    // Test select collapsing
 2192    cx.update_editor(|editor, cx| {
 2193        editor.move_page_down(&MovePageDown::default(), cx);
 2194        editor.move_page_down(&MovePageDown::default(), cx);
 2195        editor.move_page_down(&MovePageDown::default(), cx);
 2196    });
 2197    cx.assert_editor_state(
 2198        &r#"
 2199        one
 2200        two
 2201        three
 2202        four
 2203        five
 2204        six
 2205        seven
 2206        eight
 2207        nine
 2208        ˇten
 2209        ˇ"#
 2210        .unindent(),
 2211    );
 2212}
 2213
 2214#[gpui::test]
 2215async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2216    init_test(cx, |_| {});
 2217    let mut cx = EditorTestContext::new(cx).await;
 2218    cx.set_state("one «two threeˇ» four");
 2219    cx.update_editor(|editor, cx| {
 2220        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2221        assert_eq!(editor.text(cx), " four");
 2222    });
 2223}
 2224
 2225#[gpui::test]
 2226fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2227    init_test(cx, |_| {});
 2228
 2229    let view = cx.add_window(|cx| {
 2230        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2231        build_editor(buffer.clone(), cx)
 2232    });
 2233
 2234    _ = view.update(cx, |view, cx| {
 2235        view.change_selections(None, cx, |s| {
 2236            s.select_display_ranges([
 2237                // an empty selection - the preceding word fragment is deleted
 2238                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2239                // characters selected - they are deleted
 2240                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2241            ])
 2242        });
 2243        view.delete_to_previous_word_start(
 2244            &DeleteToPreviousWordStart {
 2245                ignore_newlines: false,
 2246            },
 2247            cx,
 2248        );
 2249        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2250    });
 2251
 2252    _ = view.update(cx, |view, cx| {
 2253        view.change_selections(None, cx, |s| {
 2254            s.select_display_ranges([
 2255                // an empty selection - the following word fragment is deleted
 2256                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2257                // characters selected - they are deleted
 2258                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2259            ])
 2260        });
 2261        view.delete_to_next_word_end(
 2262            &DeleteToNextWordEnd {
 2263                ignore_newlines: false,
 2264            },
 2265            cx,
 2266        );
 2267        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2268    });
 2269}
 2270
 2271#[gpui::test]
 2272fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2273    init_test(cx, |_| {});
 2274
 2275    let view = cx.add_window(|cx| {
 2276        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2277        build_editor(buffer.clone(), cx)
 2278    });
 2279    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2280        ignore_newlines: false,
 2281    };
 2282    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2283        ignore_newlines: true,
 2284    };
 2285
 2286    _ = view.update(cx, |view, cx| {
 2287        view.change_selections(None, cx, |s| {
 2288            s.select_display_ranges([
 2289                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2290            ])
 2291        });
 2292        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2293        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2294        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2295        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2296        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2297        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2298        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2299        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2300        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2301        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2302        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2303        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2304    });
 2305}
 2306
 2307#[gpui::test]
 2308fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2309    init_test(cx, |_| {});
 2310
 2311    let view = cx.add_window(|cx| {
 2312        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2313        build_editor(buffer.clone(), cx)
 2314    });
 2315    let del_to_next_word_end = DeleteToNextWordEnd {
 2316        ignore_newlines: false,
 2317    };
 2318    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2319        ignore_newlines: true,
 2320    };
 2321
 2322    _ = view.update(cx, |view, cx| {
 2323        view.change_selections(None, cx, |s| {
 2324            s.select_display_ranges([
 2325                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2326            ])
 2327        });
 2328        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2329        assert_eq!(
 2330            view.buffer.read(cx).read(cx).text(),
 2331            "one\n   two\nthree\n   four"
 2332        );
 2333        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2334        assert_eq!(
 2335            view.buffer.read(cx).read(cx).text(),
 2336            "\n   two\nthree\n   four"
 2337        );
 2338        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2339        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2340        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2341        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2342        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2343        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2344        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2345        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2346    });
 2347}
 2348
 2349#[gpui::test]
 2350fn test_newline(cx: &mut TestAppContext) {
 2351    init_test(cx, |_| {});
 2352
 2353    let view = cx.add_window(|cx| {
 2354        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2355        build_editor(buffer.clone(), cx)
 2356    });
 2357
 2358    _ = view.update(cx, |view, cx| {
 2359        view.change_selections(None, cx, |s| {
 2360            s.select_display_ranges([
 2361                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2362                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2363                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2364            ])
 2365        });
 2366
 2367        view.newline(&Newline, cx);
 2368        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2369    });
 2370}
 2371
 2372#[gpui::test]
 2373fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2374    init_test(cx, |_| {});
 2375
 2376    let editor = cx.add_window(|cx| {
 2377        let buffer = MultiBuffer::build_simple(
 2378            "
 2379                a
 2380                b(
 2381                    X
 2382                )
 2383                c(
 2384                    X
 2385                )
 2386            "
 2387            .unindent()
 2388            .as_str(),
 2389            cx,
 2390        );
 2391        let mut editor = build_editor(buffer.clone(), cx);
 2392        editor.change_selections(None, cx, |s| {
 2393            s.select_ranges([
 2394                Point::new(2, 4)..Point::new(2, 5),
 2395                Point::new(5, 4)..Point::new(5, 5),
 2396            ])
 2397        });
 2398        editor
 2399    });
 2400
 2401    _ = editor.update(cx, |editor, cx| {
 2402        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2403        editor.buffer.update(cx, |buffer, cx| {
 2404            buffer.edit(
 2405                [
 2406                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2407                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2408                ],
 2409                None,
 2410                cx,
 2411            );
 2412            assert_eq!(
 2413                buffer.read(cx).text(),
 2414                "
 2415                    a
 2416                    b()
 2417                    c()
 2418                "
 2419                .unindent()
 2420            );
 2421        });
 2422        assert_eq!(
 2423            editor.selections.ranges(cx),
 2424            &[
 2425                Point::new(1, 2)..Point::new(1, 2),
 2426                Point::new(2, 2)..Point::new(2, 2),
 2427            ],
 2428        );
 2429
 2430        editor.newline(&Newline, cx);
 2431        assert_eq!(
 2432            editor.text(cx),
 2433            "
 2434                a
 2435                b(
 2436                )
 2437                c(
 2438                )
 2439            "
 2440            .unindent()
 2441        );
 2442
 2443        // The selections are moved after the inserted newlines
 2444        assert_eq!(
 2445            editor.selections.ranges(cx),
 2446            &[
 2447                Point::new(2, 0)..Point::new(2, 0),
 2448                Point::new(4, 0)..Point::new(4, 0),
 2449            ],
 2450        );
 2451    });
 2452}
 2453
 2454#[gpui::test]
 2455async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2456    init_test(cx, |settings| {
 2457        settings.defaults.tab_size = NonZeroU32::new(4)
 2458    });
 2459
 2460    let language = Arc::new(
 2461        Language::new(
 2462            LanguageConfig::default(),
 2463            Some(tree_sitter_rust::LANGUAGE.into()),
 2464        )
 2465        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2466        .unwrap(),
 2467    );
 2468
 2469    let mut cx = EditorTestContext::new(cx).await;
 2470    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2471    cx.set_state(indoc! {"
 2472        const a: ˇA = (
 2473 2474                «const_functionˇ»(ˇ),
 2475                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2476 2477        ˇ);ˇ
 2478    "});
 2479
 2480    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2481    cx.assert_editor_state(indoc! {"
 2482        ˇ
 2483        const a: A = (
 2484            ˇ
 2485            (
 2486                ˇ
 2487                ˇ
 2488                const_function(),
 2489                ˇ
 2490                ˇ
 2491                ˇ
 2492                ˇ
 2493                something_else,
 2494                ˇ
 2495            )
 2496            ˇ
 2497            ˇ
 2498        );
 2499    "});
 2500}
 2501
 2502#[gpui::test]
 2503async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2504    init_test(cx, |settings| {
 2505        settings.defaults.tab_size = NonZeroU32::new(4)
 2506    });
 2507
 2508    let language = Arc::new(
 2509        Language::new(
 2510            LanguageConfig::default(),
 2511            Some(tree_sitter_rust::LANGUAGE.into()),
 2512        )
 2513        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2514        .unwrap(),
 2515    );
 2516
 2517    let mut cx = EditorTestContext::new(cx).await;
 2518    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2519    cx.set_state(indoc! {"
 2520        const a: ˇA = (
 2521 2522                «const_functionˇ»(ˇ),
 2523                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2524 2525        ˇ);ˇ
 2526    "});
 2527
 2528    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2529    cx.assert_editor_state(indoc! {"
 2530        const a: A = (
 2531            ˇ
 2532            (
 2533                ˇ
 2534                const_function(),
 2535                ˇ
 2536                ˇ
 2537                something_else,
 2538                ˇ
 2539                ˇ
 2540                ˇ
 2541                ˇ
 2542            )
 2543            ˇ
 2544        );
 2545        ˇ
 2546        ˇ
 2547    "});
 2548}
 2549
 2550#[gpui::test]
 2551async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2552    init_test(cx, |settings| {
 2553        settings.defaults.tab_size = NonZeroU32::new(4)
 2554    });
 2555
 2556    let language = Arc::new(Language::new(
 2557        LanguageConfig {
 2558            line_comments: vec!["//".into()],
 2559            ..LanguageConfig::default()
 2560        },
 2561        None,
 2562    ));
 2563    {
 2564        let mut cx = EditorTestContext::new(cx).await;
 2565        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2566        cx.set_state(indoc! {"
 2567        // Fooˇ
 2568    "});
 2569
 2570        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2571        cx.assert_editor_state(indoc! {"
 2572        // Foo
 2573        //ˇ
 2574    "});
 2575        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2576        cx.set_state(indoc! {"
 2577        ˇ// Foo
 2578    "});
 2579        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2580        cx.assert_editor_state(indoc! {"
 2581
 2582        ˇ// Foo
 2583    "});
 2584    }
 2585    // Ensure that comment continuations can be disabled.
 2586    update_test_language_settings(cx, |settings| {
 2587        settings.defaults.extend_comment_on_newline = Some(false);
 2588    });
 2589    let mut cx = EditorTestContext::new(cx).await;
 2590    cx.set_state(indoc! {"
 2591        // Fooˇ
 2592    "});
 2593    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2594    cx.assert_editor_state(indoc! {"
 2595        // Foo
 2596        ˇ
 2597    "});
 2598}
 2599
 2600#[gpui::test]
 2601fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2602    init_test(cx, |_| {});
 2603
 2604    let editor = cx.add_window(|cx| {
 2605        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2606        let mut editor = build_editor(buffer.clone(), cx);
 2607        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2608        editor
 2609    });
 2610
 2611    _ = editor.update(cx, |editor, cx| {
 2612        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2613        editor.buffer.update(cx, |buffer, cx| {
 2614            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2615            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2616        });
 2617        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2618
 2619        editor.insert("Z", cx);
 2620        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2621
 2622        // The selections are moved after the inserted characters
 2623        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2624    });
 2625}
 2626
 2627#[gpui::test]
 2628async fn test_tab(cx: &mut gpui::TestAppContext) {
 2629    init_test(cx, |settings| {
 2630        settings.defaults.tab_size = NonZeroU32::new(3)
 2631    });
 2632
 2633    let mut cx = EditorTestContext::new(cx).await;
 2634    cx.set_state(indoc! {"
 2635        ˇabˇc
 2636        ˇ🏀ˇ🏀ˇefg
 2637 2638    "});
 2639    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2640    cx.assert_editor_state(indoc! {"
 2641           ˇab ˇc
 2642           ˇ🏀  ˇ🏀  ˇefg
 2643        d  ˇ
 2644    "});
 2645
 2646    cx.set_state(indoc! {"
 2647        a
 2648        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        a
 2653           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2654    "});
 2655}
 2656
 2657#[gpui::test]
 2658async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2659    init_test(cx, |_| {});
 2660
 2661    let mut cx = EditorTestContext::new(cx).await;
 2662    let language = Arc::new(
 2663        Language::new(
 2664            LanguageConfig::default(),
 2665            Some(tree_sitter_rust::LANGUAGE.into()),
 2666        )
 2667        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2668        .unwrap(),
 2669    );
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671
 2672    // cursors that are already at the suggested indent level insert
 2673    // a soft tab. cursors that are to the left of the suggested indent
 2674    // auto-indent their line.
 2675    cx.set_state(indoc! {"
 2676        ˇ
 2677        const a: B = (
 2678            c(
 2679                d(
 2680        ˇ
 2681                )
 2682        ˇ
 2683        ˇ    )
 2684        );
 2685    "});
 2686    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2687    cx.assert_editor_state(indoc! {"
 2688            ˇ
 2689        const a: B = (
 2690            c(
 2691                d(
 2692                    ˇ
 2693                )
 2694                ˇ
 2695            ˇ)
 2696        );
 2697    "});
 2698
 2699    // handle auto-indent when there are multiple cursors on the same line
 2700    cx.set_state(indoc! {"
 2701        const a: B = (
 2702            c(
 2703        ˇ    ˇ
 2704        ˇ    )
 2705        );
 2706    "});
 2707    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2708    cx.assert_editor_state(indoc! {"
 2709        const a: B = (
 2710            c(
 2711                ˇ
 2712            ˇ)
 2713        );
 2714    "});
 2715}
 2716
 2717#[gpui::test]
 2718async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2719    init_test(cx, |settings| {
 2720        settings.defaults.tab_size = NonZeroU32::new(4)
 2721    });
 2722
 2723    let language = Arc::new(
 2724        Language::new(
 2725            LanguageConfig::default(),
 2726            Some(tree_sitter_rust::LANGUAGE.into()),
 2727        )
 2728        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2729        .unwrap(),
 2730    );
 2731
 2732    let mut cx = EditorTestContext::new(cx).await;
 2733    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2734    cx.set_state(indoc! {"
 2735        fn a() {
 2736            if b {
 2737        \t ˇc
 2738            }
 2739        }
 2740    "});
 2741
 2742    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2743    cx.assert_editor_state(indoc! {"
 2744        fn a() {
 2745            if b {
 2746                ˇc
 2747            }
 2748        }
 2749    "});
 2750}
 2751
 2752#[gpui::test]
 2753async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2754    init_test(cx, |settings| {
 2755        settings.defaults.tab_size = NonZeroU32::new(4);
 2756    });
 2757
 2758    let mut cx = EditorTestContext::new(cx).await;
 2759
 2760    cx.set_state(indoc! {"
 2761          «oneˇ» «twoˇ»
 2762        three
 2763         four
 2764    "});
 2765    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2766    cx.assert_editor_state(indoc! {"
 2767            «oneˇ» «twoˇ»
 2768        three
 2769         four
 2770    "});
 2771
 2772    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2773    cx.assert_editor_state(indoc! {"
 2774        «oneˇ» «twoˇ»
 2775        three
 2776         four
 2777    "});
 2778
 2779    // select across line ending
 2780    cx.set_state(indoc! {"
 2781        one two
 2782        t«hree
 2783        ˇ» four
 2784    "});
 2785    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2786    cx.assert_editor_state(indoc! {"
 2787        one two
 2788            t«hree
 2789        ˇ» four
 2790    "});
 2791
 2792    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        one two
 2795        t«hree
 2796        ˇ» four
 2797    "});
 2798
 2799    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2800    cx.set_state(indoc! {"
 2801        one two
 2802        ˇthree
 2803            four
 2804    "});
 2805    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2806    cx.assert_editor_state(indoc! {"
 2807        one two
 2808            ˇthree
 2809            four
 2810    "});
 2811
 2812    cx.set_state(indoc! {"
 2813        one two
 2814        ˇ    three
 2815            four
 2816    "});
 2817    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2818    cx.assert_editor_state(indoc! {"
 2819        one two
 2820        ˇthree
 2821            four
 2822    "});
 2823}
 2824
 2825#[gpui::test]
 2826async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2827    init_test(cx, |settings| {
 2828        settings.defaults.hard_tabs = Some(true);
 2829    });
 2830
 2831    let mut cx = EditorTestContext::new(cx).await;
 2832
 2833    // select two ranges on one line
 2834    cx.set_state(indoc! {"
 2835        «oneˇ» «twoˇ»
 2836        three
 2837        four
 2838    "});
 2839    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2840    cx.assert_editor_state(indoc! {"
 2841        \t«oneˇ» «twoˇ»
 2842        three
 2843        four
 2844    "});
 2845    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2846    cx.assert_editor_state(indoc! {"
 2847        \t\t«oneˇ» «twoˇ»
 2848        three
 2849        four
 2850    "});
 2851    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        \t«oneˇ» «twoˇ»
 2854        three
 2855        four
 2856    "});
 2857    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2858    cx.assert_editor_state(indoc! {"
 2859        «oneˇ» «twoˇ»
 2860        three
 2861        four
 2862    "});
 2863
 2864    // select across a line ending
 2865    cx.set_state(indoc! {"
 2866        one two
 2867        t«hree
 2868        ˇ»four
 2869    "});
 2870    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2871    cx.assert_editor_state(indoc! {"
 2872        one two
 2873        \tt«hree
 2874        ˇ»four
 2875    "});
 2876    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2877    cx.assert_editor_state(indoc! {"
 2878        one two
 2879        \t\tt«hree
 2880        ˇ»four
 2881    "});
 2882    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884        one two
 2885        \tt«hree
 2886        ˇ»four
 2887    "});
 2888    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2889    cx.assert_editor_state(indoc! {"
 2890        one two
 2891        t«hree
 2892        ˇ»four
 2893    "});
 2894
 2895    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2896    cx.set_state(indoc! {"
 2897        one two
 2898        ˇthree
 2899        four
 2900    "});
 2901    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2902    cx.assert_editor_state(indoc! {"
 2903        one two
 2904        ˇthree
 2905        four
 2906    "});
 2907    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2908    cx.assert_editor_state(indoc! {"
 2909        one two
 2910        \tˇthree
 2911        four
 2912    "});
 2913    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2914    cx.assert_editor_state(indoc! {"
 2915        one two
 2916        ˇthree
 2917        four
 2918    "});
 2919}
 2920
 2921#[gpui::test]
 2922fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2923    init_test(cx, |settings| {
 2924        settings.languages.extend([
 2925            (
 2926                "TOML".into(),
 2927                LanguageSettingsContent {
 2928                    tab_size: NonZeroU32::new(2),
 2929                    ..Default::default()
 2930                },
 2931            ),
 2932            (
 2933                "Rust".into(),
 2934                LanguageSettingsContent {
 2935                    tab_size: NonZeroU32::new(4),
 2936                    ..Default::default()
 2937                },
 2938            ),
 2939        ]);
 2940    });
 2941
 2942    let toml_language = Arc::new(Language::new(
 2943        LanguageConfig {
 2944            name: "TOML".into(),
 2945            ..Default::default()
 2946        },
 2947        None,
 2948    ));
 2949    let rust_language = Arc::new(Language::new(
 2950        LanguageConfig {
 2951            name: "Rust".into(),
 2952            ..Default::default()
 2953        },
 2954        None,
 2955    ));
 2956
 2957    let toml_buffer =
 2958        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2959    let rust_buffer = cx.new_model(|cx| {
 2960        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2961    });
 2962    let multibuffer = cx.new_model(|cx| {
 2963        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2964        multibuffer.push_excerpts(
 2965            toml_buffer.clone(),
 2966            [ExcerptRange {
 2967                context: Point::new(0, 0)..Point::new(2, 0),
 2968                primary: None,
 2969            }],
 2970            cx,
 2971        );
 2972        multibuffer.push_excerpts(
 2973            rust_buffer.clone(),
 2974            [ExcerptRange {
 2975                context: Point::new(0, 0)..Point::new(1, 0),
 2976                primary: None,
 2977            }],
 2978            cx,
 2979        );
 2980        multibuffer
 2981    });
 2982
 2983    cx.add_window(|cx| {
 2984        let mut editor = build_editor(multibuffer, cx);
 2985
 2986        assert_eq!(
 2987            editor.text(cx),
 2988            indoc! {"
 2989                a = 1
 2990                b = 2
 2991
 2992                const c: usize = 3;
 2993            "}
 2994        );
 2995
 2996        select_ranges(
 2997            &mut editor,
 2998            indoc! {"
 2999                «aˇ» = 1
 3000                b = 2
 3001
 3002                «const c:ˇ» usize = 3;
 3003            "},
 3004            cx,
 3005        );
 3006
 3007        editor.tab(&Tab, cx);
 3008        assert_text_with_selections(
 3009            &mut editor,
 3010            indoc! {"
 3011                  «aˇ» = 1
 3012                b = 2
 3013
 3014                    «const c:ˇ» usize = 3;
 3015            "},
 3016            cx,
 3017        );
 3018        editor.tab_prev(&TabPrev, cx);
 3019        assert_text_with_selections(
 3020            &mut editor,
 3021            indoc! {"
 3022                «aˇ» = 1
 3023                b = 2
 3024
 3025                «const c:ˇ» usize = 3;
 3026            "},
 3027            cx,
 3028        );
 3029
 3030        editor
 3031    });
 3032}
 3033
 3034#[gpui::test]
 3035async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3036    init_test(cx, |_| {});
 3037
 3038    let mut cx = EditorTestContext::new(cx).await;
 3039
 3040    // Basic backspace
 3041    cx.set_state(indoc! {"
 3042        onˇe two three
 3043        fou«rˇ» five six
 3044        seven «ˇeight nine
 3045        »ten
 3046    "});
 3047    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3048    cx.assert_editor_state(indoc! {"
 3049        oˇe two three
 3050        fouˇ five six
 3051        seven ˇten
 3052    "});
 3053
 3054    // Test backspace inside and around indents
 3055    cx.set_state(indoc! {"
 3056        zero
 3057            ˇone
 3058                ˇtwo
 3059            ˇ ˇ ˇ  three
 3060        ˇ  ˇ  four
 3061    "});
 3062    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3063    cx.assert_editor_state(indoc! {"
 3064        zero
 3065        ˇone
 3066            ˇtwo
 3067        ˇ  threeˇ  four
 3068    "});
 3069
 3070    // Test backspace with line_mode set to true
 3071    cx.update_editor(|e, _| e.selections.line_mode = true);
 3072    cx.set_state(indoc! {"
 3073        The ˇquick ˇbrown
 3074        fox jumps over
 3075        the lazy dog
 3076        ˇThe qu«ick bˇ»rown"});
 3077    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        ˇfox jumps over
 3080        the lazy dogˇ"});
 3081}
 3082
 3083#[gpui::test]
 3084async fn test_delete(cx: &mut gpui::TestAppContext) {
 3085    init_test(cx, |_| {});
 3086
 3087    let mut cx = EditorTestContext::new(cx).await;
 3088    cx.set_state(indoc! {"
 3089        onˇe two three
 3090        fou«rˇ» five six
 3091        seven «ˇeight nine
 3092        »ten
 3093    "});
 3094    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3095    cx.assert_editor_state(indoc! {"
 3096        onˇ two three
 3097        fouˇ five six
 3098        seven ˇten
 3099    "});
 3100
 3101    // Test backspace with line_mode set to true
 3102    cx.update_editor(|e, _| e.selections.line_mode = true);
 3103    cx.set_state(indoc! {"
 3104        The ˇquick ˇbrown
 3105        fox «ˇjum»ps over
 3106        the lazy dog
 3107        ˇThe qu«ick bˇ»rown"});
 3108    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3109    cx.assert_editor_state("ˇthe lazy dogˇ");
 3110}
 3111
 3112#[gpui::test]
 3113fn test_delete_line(cx: &mut TestAppContext) {
 3114    init_test(cx, |_| {});
 3115
 3116    let view = cx.add_window(|cx| {
 3117        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3118        build_editor(buffer, cx)
 3119    });
 3120    _ = view.update(cx, |view, cx| {
 3121        view.change_selections(None, cx, |s| {
 3122            s.select_display_ranges([
 3123                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3125                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3126            ])
 3127        });
 3128        view.delete_line(&DeleteLine, cx);
 3129        assert_eq!(view.display_text(cx), "ghi");
 3130        assert_eq!(
 3131            view.selections.display_ranges(cx),
 3132            vec![
 3133                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3134                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3135            ]
 3136        );
 3137    });
 3138
 3139    let view = cx.add_window(|cx| {
 3140        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3141        build_editor(buffer, cx)
 3142    });
 3143    _ = view.update(cx, |view, cx| {
 3144        view.change_selections(None, cx, |s| {
 3145            s.select_display_ranges([
 3146                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3147            ])
 3148        });
 3149        view.delete_line(&DeleteLine, cx);
 3150        assert_eq!(view.display_text(cx), "ghi\n");
 3151        assert_eq!(
 3152            view.selections.display_ranges(cx),
 3153            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3154        );
 3155    });
 3156}
 3157
 3158#[gpui::test]
 3159fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3160    init_test(cx, |_| {});
 3161
 3162    cx.add_window(|cx| {
 3163        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3164        let mut editor = build_editor(buffer.clone(), cx);
 3165        let buffer = buffer.read(cx).as_singleton().unwrap();
 3166
 3167        assert_eq!(
 3168            editor.selections.ranges::<Point>(cx),
 3169            &[Point::new(0, 0)..Point::new(0, 0)]
 3170        );
 3171
 3172        // When on single line, replace newline at end by space
 3173        editor.join_lines(&JoinLines, cx);
 3174        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3175        assert_eq!(
 3176            editor.selections.ranges::<Point>(cx),
 3177            &[Point::new(0, 3)..Point::new(0, 3)]
 3178        );
 3179
 3180        // When multiple lines are selected, remove newlines that are spanned by the selection
 3181        editor.change_selections(None, cx, |s| {
 3182            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3183        });
 3184        editor.join_lines(&JoinLines, cx);
 3185        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3186        assert_eq!(
 3187            editor.selections.ranges::<Point>(cx),
 3188            &[Point::new(0, 11)..Point::new(0, 11)]
 3189        );
 3190
 3191        // Undo should be transactional
 3192        editor.undo(&Undo, cx);
 3193        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3194        assert_eq!(
 3195            editor.selections.ranges::<Point>(cx),
 3196            &[Point::new(0, 5)..Point::new(2, 2)]
 3197        );
 3198
 3199        // When joining an empty line don't insert a space
 3200        editor.change_selections(None, cx, |s| {
 3201            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3202        });
 3203        editor.join_lines(&JoinLines, cx);
 3204        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3205        assert_eq!(
 3206            editor.selections.ranges::<Point>(cx),
 3207            [Point::new(2, 3)..Point::new(2, 3)]
 3208        );
 3209
 3210        // We can remove trailing newlines
 3211        editor.join_lines(&JoinLines, cx);
 3212        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3213        assert_eq!(
 3214            editor.selections.ranges::<Point>(cx),
 3215            [Point::new(2, 3)..Point::new(2, 3)]
 3216        );
 3217
 3218        // We don't blow up on the last line
 3219        editor.join_lines(&JoinLines, cx);
 3220        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3221        assert_eq!(
 3222            editor.selections.ranges::<Point>(cx),
 3223            [Point::new(2, 3)..Point::new(2, 3)]
 3224        );
 3225
 3226        // reset to test indentation
 3227        editor.buffer.update(cx, |buffer, cx| {
 3228            buffer.edit(
 3229                [
 3230                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3231                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3232                ],
 3233                None,
 3234                cx,
 3235            )
 3236        });
 3237
 3238        // We remove any leading spaces
 3239        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3240        editor.change_selections(None, cx, |s| {
 3241            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3242        });
 3243        editor.join_lines(&JoinLines, cx);
 3244        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3245
 3246        // We don't insert a space for a line containing only spaces
 3247        editor.join_lines(&JoinLines, cx);
 3248        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3249
 3250        // We ignore any leading tabs
 3251        editor.join_lines(&JoinLines, cx);
 3252        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3253
 3254        editor
 3255    });
 3256}
 3257
 3258#[gpui::test]
 3259fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3260    init_test(cx, |_| {});
 3261
 3262    cx.add_window(|cx| {
 3263        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3264        let mut editor = build_editor(buffer.clone(), cx);
 3265        let buffer = buffer.read(cx).as_singleton().unwrap();
 3266
 3267        editor.change_selections(None, cx, |s| {
 3268            s.select_ranges([
 3269                Point::new(0, 2)..Point::new(1, 1),
 3270                Point::new(1, 2)..Point::new(1, 2),
 3271                Point::new(3, 1)..Point::new(3, 2),
 3272            ])
 3273        });
 3274
 3275        editor.join_lines(&JoinLines, cx);
 3276        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3277
 3278        assert_eq!(
 3279            editor.selections.ranges::<Point>(cx),
 3280            [
 3281                Point::new(0, 7)..Point::new(0, 7),
 3282                Point::new(1, 3)..Point::new(1, 3)
 3283            ]
 3284        );
 3285        editor
 3286    });
 3287}
 3288
 3289#[gpui::test]
 3290async fn test_join_lines_with_git_diff_base(
 3291    executor: BackgroundExecutor,
 3292    cx: &mut gpui::TestAppContext,
 3293) {
 3294    init_test(cx, |_| {});
 3295
 3296    let mut cx = EditorTestContext::new(cx).await;
 3297
 3298    let diff_base = r#"
 3299        Line 0
 3300        Line 1
 3301        Line 2
 3302        Line 3
 3303        "#
 3304    .unindent();
 3305
 3306    cx.set_state(
 3307        &r#"
 3308        ˇLine 0
 3309        Line 1
 3310        Line 2
 3311        Line 3
 3312        "#
 3313        .unindent(),
 3314    );
 3315
 3316    cx.set_diff_base(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 resolve_requests_number = Arc::new(AtomicUsize::new(0));
10664    let expect_first_item = Arc::new(AtomicBool::new(true));
10665    cx.lsp
10666        .server
10667        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10668            let closure_default_data = default_data.clone();
10669            let closure_resolve_requests_number = resolve_requests_number.clone();
10670            let closure_expect_first_item = expect_first_item.clone();
10671            let closure_default_commit_characters = default_commit_characters.clone();
10672            move |item_to_resolve, _| {
10673                closure_resolve_requests_number.fetch_add(1, atomic::Ordering::Release);
10674                let default_data = closure_default_data.clone();
10675                let default_commit_characters = closure_default_commit_characters.clone();
10676                let expect_first_item = closure_expect_first_item.clone();
10677                async move {
10678                    if expect_first_item.load(atomic::Ordering::Acquire) {
10679                        assert_eq!(
10680                            item_to_resolve.label, "Some(2)",
10681                            "Should have selected the first item"
10682                        );
10683                        assert_eq!(
10684                            item_to_resolve.data,
10685                            Some(json!({ "very": "special"})),
10686                            "First item should bring its own data for resolving"
10687                        );
10688                        assert_eq!(
10689                            item_to_resolve.commit_characters,
10690                            Some(default_commit_characters),
10691                            "First item had no own commit characters and should inherit the default ones"
10692                        );
10693                        assert!(
10694                            matches!(
10695                                item_to_resolve.text_edit,
10696                                Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10697                            ),
10698                            "First item should bring its own edit range for resolving"
10699                        );
10700                        assert_eq!(
10701                            item_to_resolve.insert_text_format,
10702                            Some(default_insert_text_format),
10703                            "First item had no own insert text format and should inherit the default one"
10704                        );
10705                        assert_eq!(
10706                            item_to_resolve.insert_text_mode,
10707                            Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10708                            "First item should bring its own insert text mode for resolving"
10709                        );
10710                        Ok(item_to_resolve)
10711                    } else {
10712                        assert_eq!(
10713                            item_to_resolve.label, "vec![2]",
10714                            "Should have selected the last item"
10715                        );
10716                        assert_eq!(
10717                            item_to_resolve.data,
10718                            Some(default_data),
10719                            "Last item has no own resolve data and should inherit the default one"
10720                        );
10721                        assert_eq!(
10722                            item_to_resolve.commit_characters,
10723                            Some(default_commit_characters),
10724                            "Last item had no own commit characters and should inherit the default ones"
10725                        );
10726                        assert_eq!(
10727                            item_to_resolve.text_edit,
10728                            Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10729                                range: default_edit_range,
10730                                new_text: "vec![2]".to_string()
10731                            })),
10732                            "Last item had no own edit range and should inherit the default one"
10733                        );
10734                        assert_eq!(
10735                            item_to_resolve.insert_text_format,
10736                            Some(lsp::InsertTextFormat::PLAIN_TEXT),
10737                            "Last item should bring its own insert text format for resolving"
10738                        );
10739                        assert_eq!(
10740                            item_to_resolve.insert_text_mode,
10741                            Some(default_insert_text_mode),
10742                            "Last item had no own insert text mode and should inherit the default one"
10743                        );
10744
10745                        Ok(item_to_resolve)
10746                    }
10747                }
10748            }
10749        }).detach();
10750
10751    let completion_data = default_data.clone();
10752    let completion_characters = default_commit_characters.clone();
10753    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10754        let default_data = completion_data.clone();
10755        let default_commit_characters = completion_characters.clone();
10756        async move {
10757            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10758                items: vec![
10759                    lsp::CompletionItem {
10760                        label: "Some(2)".into(),
10761                        insert_text: Some("Some(2)".into()),
10762                        data: Some(json!({ "very": "special"})),
10763                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10764                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10765                            lsp::InsertReplaceEdit {
10766                                new_text: "Some(2)".to_string(),
10767                                insert: lsp::Range::default(),
10768                                replace: lsp::Range::default(),
10769                            },
10770                        )),
10771                        ..lsp::CompletionItem::default()
10772                    },
10773                    lsp::CompletionItem {
10774                        label: "vec![2]".into(),
10775                        insert_text: Some("vec![2]".into()),
10776                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10777                        ..lsp::CompletionItem::default()
10778                    },
10779                ],
10780                item_defaults: Some(lsp::CompletionListItemDefaults {
10781                    data: Some(default_data.clone()),
10782                    commit_characters: Some(default_commit_characters.clone()),
10783                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10784                        default_edit_range,
10785                    )),
10786                    insert_text_format: Some(default_insert_text_format),
10787                    insert_text_mode: Some(default_insert_text_mode),
10788                }),
10789                ..lsp::CompletionList::default()
10790            })))
10791        }
10792    })
10793    .next()
10794    .await;
10795
10796    cx.condition(|editor, _| editor.context_menu_visible())
10797        .await;
10798    cx.run_until_parked();
10799    cx.update_editor(|editor, _| {
10800        let menu = editor.context_menu.read();
10801        match menu.as_ref().expect("should have the completions menu") {
10802            ContextMenu::Completions(completions_menu) => {
10803                assert_eq!(
10804                    completions_menu
10805                        .matches
10806                        .iter()
10807                        .map(|c| c.string.as_str())
10808                        .collect::<Vec<_>>(),
10809                    vec!["Some(2)", "vec![2]"]
10810                );
10811            }
10812            ContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10813        }
10814    });
10815    assert_eq!(
10816        resolve_requests_number.load(atomic::Ordering::Acquire),
10817        1,
10818        "While there are 2 items in the completion list, only 1 resolve request should have been sent, for the selected item"
10819    );
10820
10821    cx.update_editor(|editor, cx| {
10822        editor.context_menu_first(&ContextMenuFirst, cx);
10823    });
10824    cx.run_until_parked();
10825    assert_eq!(
10826        resolve_requests_number.load(atomic::Ordering::Acquire),
10827        2,
10828        "After re-selecting the first item, another resolve request should have been sent"
10829    );
10830
10831    expect_first_item.store(false, atomic::Ordering::Release);
10832    cx.update_editor(|editor, cx| {
10833        editor.context_menu_last(&ContextMenuLast, cx);
10834    });
10835    cx.run_until_parked();
10836    assert_eq!(
10837        resolve_requests_number.load(atomic::Ordering::Acquire),
10838        3,
10839        "After selecting the other item, another resolve request should have been sent"
10840    );
10841}
10842
10843#[gpui::test]
10844async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10845    init_test(cx, |_| {});
10846
10847    let mut cx = EditorLspTestContext::new(
10848        Language::new(
10849            LanguageConfig {
10850                matcher: LanguageMatcher {
10851                    path_suffixes: vec!["jsx".into()],
10852                    ..Default::default()
10853                },
10854                overrides: [(
10855                    "element".into(),
10856                    LanguageConfigOverride {
10857                        word_characters: Override::Set(['-'].into_iter().collect()),
10858                        ..Default::default()
10859                    },
10860                )]
10861                .into_iter()
10862                .collect(),
10863                ..Default::default()
10864            },
10865            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10866        )
10867        .with_override_query("(jsx_self_closing_element) @element")
10868        .unwrap(),
10869        lsp::ServerCapabilities {
10870            completion_provider: Some(lsp::CompletionOptions {
10871                trigger_characters: Some(vec![":".to_string()]),
10872                ..Default::default()
10873            }),
10874            ..Default::default()
10875        },
10876        cx,
10877    )
10878    .await;
10879
10880    cx.lsp
10881        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10882            Ok(Some(lsp::CompletionResponse::Array(vec![
10883                lsp::CompletionItem {
10884                    label: "bg-blue".into(),
10885                    ..Default::default()
10886                },
10887                lsp::CompletionItem {
10888                    label: "bg-red".into(),
10889                    ..Default::default()
10890                },
10891                lsp::CompletionItem {
10892                    label: "bg-yellow".into(),
10893                    ..Default::default()
10894                },
10895            ])))
10896        });
10897
10898    cx.set_state(r#"<p class="bgˇ" />"#);
10899
10900    // Trigger completion when typing a dash, because the dash is an extra
10901    // word character in the 'element' scope, which contains the cursor.
10902    cx.simulate_keystroke("-");
10903    cx.executor().run_until_parked();
10904    cx.update_editor(|editor, _| {
10905        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10906            assert_eq!(
10907                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10908                &["bg-red", "bg-blue", "bg-yellow"]
10909            );
10910        } else {
10911            panic!("expected completion menu to be open");
10912        }
10913    });
10914
10915    cx.simulate_keystroke("l");
10916    cx.executor().run_until_parked();
10917    cx.update_editor(|editor, _| {
10918        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10919            assert_eq!(
10920                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10921                &["bg-blue", "bg-yellow"]
10922            );
10923        } else {
10924            panic!("expected completion menu to be open");
10925        }
10926    });
10927
10928    // When filtering completions, consider the character after the '-' to
10929    // be the start of a subword.
10930    cx.set_state(r#"<p class="yelˇ" />"#);
10931    cx.simulate_keystroke("l");
10932    cx.executor().run_until_parked();
10933    cx.update_editor(|editor, _| {
10934        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10935            assert_eq!(
10936                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10937                &["bg-yellow"]
10938            );
10939        } else {
10940            panic!("expected completion menu to be open");
10941        }
10942    });
10943}
10944
10945#[gpui::test]
10946async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10947    init_test(cx, |settings| {
10948        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10949            FormatterList(vec![Formatter::Prettier].into()),
10950        ))
10951    });
10952
10953    let fs = FakeFs::new(cx.executor());
10954    fs.insert_file("/file.ts", Default::default()).await;
10955
10956    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10957    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10958
10959    language_registry.add(Arc::new(Language::new(
10960        LanguageConfig {
10961            name: "TypeScript".into(),
10962            matcher: LanguageMatcher {
10963                path_suffixes: vec!["ts".to_string()],
10964                ..Default::default()
10965            },
10966            ..Default::default()
10967        },
10968        Some(tree_sitter_rust::LANGUAGE.into()),
10969    )));
10970    update_test_language_settings(cx, |settings| {
10971        settings.defaults.prettier = Some(PrettierSettings {
10972            allowed: true,
10973            ..PrettierSettings::default()
10974        });
10975    });
10976
10977    let test_plugin = "test_plugin";
10978    let _ = language_registry.register_fake_lsp(
10979        "TypeScript",
10980        FakeLspAdapter {
10981            prettier_plugins: vec![test_plugin],
10982            ..Default::default()
10983        },
10984    );
10985
10986    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10987    let buffer = project
10988        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10989        .await
10990        .unwrap();
10991
10992    let buffer_text = "one\ntwo\nthree\n";
10993    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10994    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10995    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10996
10997    editor
10998        .update(cx, |editor, cx| {
10999            editor.perform_format(
11000                project.clone(),
11001                FormatTrigger::Manual,
11002                FormatTarget::Buffer,
11003                cx,
11004            )
11005        })
11006        .unwrap()
11007        .await;
11008    assert_eq!(
11009        editor.update(cx, |editor, cx| editor.text(cx)),
11010        buffer_text.to_string() + prettier_format_suffix,
11011        "Test prettier formatting was not applied to the original buffer text",
11012    );
11013
11014    update_test_language_settings(cx, |settings| {
11015        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11016    });
11017    let format = editor.update(cx, |editor, cx| {
11018        editor.perform_format(
11019            project.clone(),
11020            FormatTrigger::Manual,
11021            FormatTarget::Buffer,
11022            cx,
11023        )
11024    });
11025    format.await.unwrap();
11026    assert_eq!(
11027        editor.update(cx, |editor, cx| editor.text(cx)),
11028        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11029        "Autoformatting (via test prettier) was not applied to the original buffer text",
11030    );
11031}
11032
11033#[gpui::test]
11034async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11035    init_test(cx, |_| {});
11036    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11037    let base_text = indoc! {r#"struct Row;
11038struct Row1;
11039struct Row2;
11040
11041struct Row4;
11042struct Row5;
11043struct Row6;
11044
11045struct Row8;
11046struct Row9;
11047struct Row10;"#};
11048
11049    // When addition hunks are not adjacent to carets, no hunk revert is performed
11050    assert_hunk_revert(
11051        indoc! {r#"struct Row;
11052                   struct Row1;
11053                   struct Row1.1;
11054                   struct Row1.2;
11055                   struct Row2;ˇ
11056
11057                   struct Row4;
11058                   struct Row5;
11059                   struct Row6;
11060
11061                   struct Row8;
11062                   ˇstruct Row9;
11063                   struct Row9.1;
11064                   struct Row9.2;
11065                   struct Row9.3;
11066                   struct Row10;"#},
11067        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11068        indoc! {r#"struct Row;
11069                   struct Row1;
11070                   struct Row1.1;
11071                   struct Row1.2;
11072                   struct Row2;ˇ
11073
11074                   struct Row4;
11075                   struct Row5;
11076                   struct Row6;
11077
11078                   struct Row8;
11079                   ˇstruct Row9;
11080                   struct Row9.1;
11081                   struct Row9.2;
11082                   struct Row9.3;
11083                   struct Row10;"#},
11084        base_text,
11085        &mut cx,
11086    );
11087    // Same for selections
11088    assert_hunk_revert(
11089        indoc! {r#"struct Row;
11090                   struct Row1;
11091                   struct Row2;
11092                   struct Row2.1;
11093                   struct Row2.2;
11094                   «ˇ
11095                   struct Row4;
11096                   struct» Row5;
11097                   «struct Row6;
11098                   ˇ»
11099                   struct Row9.1;
11100                   struct Row9.2;
11101                   struct Row9.3;
11102                   struct Row8;
11103                   struct Row9;
11104                   struct Row10;"#},
11105        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11106        indoc! {r#"struct Row;
11107                   struct Row1;
11108                   struct Row2;
11109                   struct Row2.1;
11110                   struct Row2.2;
11111                   «ˇ
11112                   struct Row4;
11113                   struct» Row5;
11114                   «struct Row6;
11115                   ˇ»
11116                   struct Row9.1;
11117                   struct Row9.2;
11118                   struct Row9.3;
11119                   struct Row8;
11120                   struct Row9;
11121                   struct Row10;"#},
11122        base_text,
11123        &mut cx,
11124    );
11125
11126    // When carets and selections intersect the addition hunks, those are reverted.
11127    // Adjacent carets got merged.
11128    assert_hunk_revert(
11129        indoc! {r#"struct Row;
11130                   ˇ// something on the top
11131                   struct Row1;
11132                   struct Row2;
11133                   struct Roˇw3.1;
11134                   struct Row2.2;
11135                   struct Row2.3;ˇ
11136
11137                   struct Row4;
11138                   struct ˇRow5.1;
11139                   struct Row5.2;
11140                   struct «Rowˇ»5.3;
11141                   struct Row5;
11142                   struct Row6;
11143                   ˇ
11144                   struct Row9.1;
11145                   struct «Rowˇ»9.2;
11146                   struct «ˇRow»9.3;
11147                   struct Row8;
11148                   struct Row9;
11149                   «ˇ// something on bottom»
11150                   struct Row10;"#},
11151        vec![
11152            DiffHunkStatus::Added,
11153            DiffHunkStatus::Added,
11154            DiffHunkStatus::Added,
11155            DiffHunkStatus::Added,
11156            DiffHunkStatus::Added,
11157        ],
11158        indoc! {r#"struct Row;
11159                   ˇstruct Row1;
11160                   struct Row2;
11161                   ˇ
11162                   struct Row4;
11163                   ˇstruct Row5;
11164                   struct Row6;
11165                   ˇ
11166                   ˇstruct Row8;
11167                   struct Row9;
11168                   ˇstruct Row10;"#},
11169        base_text,
11170        &mut cx,
11171    );
11172}
11173
11174#[gpui::test]
11175async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11176    init_test(cx, |_| {});
11177    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11178    let base_text = indoc! {r#"struct Row;
11179struct Row1;
11180struct Row2;
11181
11182struct Row4;
11183struct Row5;
11184struct Row6;
11185
11186struct Row8;
11187struct Row9;
11188struct Row10;"#};
11189
11190    // Modification hunks behave the same as the addition ones.
11191    assert_hunk_revert(
11192        indoc! {r#"struct Row;
11193                   struct Row1;
11194                   struct Row33;
11195                   ˇ
11196                   struct Row4;
11197                   struct Row5;
11198                   struct Row6;
11199                   ˇ
11200                   struct Row99;
11201                   struct Row9;
11202                   struct Row10;"#},
11203        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11204        indoc! {r#"struct Row;
11205                   struct Row1;
11206                   struct Row33;
11207                   ˇ
11208                   struct Row4;
11209                   struct Row5;
11210                   struct Row6;
11211                   ˇ
11212                   struct Row99;
11213                   struct Row9;
11214                   struct Row10;"#},
11215        base_text,
11216        &mut cx,
11217    );
11218    assert_hunk_revert(
11219        indoc! {r#"struct Row;
11220                   struct Row1;
11221                   struct Row33;
11222                   «ˇ
11223                   struct Row4;
11224                   struct» Row5;
11225                   «struct Row6;
11226                   ˇ»
11227                   struct Row99;
11228                   struct Row9;
11229                   struct Row10;"#},
11230        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11231        indoc! {r#"struct Row;
11232                   struct Row1;
11233                   struct Row33;
11234                   «ˇ
11235                   struct Row4;
11236                   struct» Row5;
11237                   «struct Row6;
11238                   ˇ»
11239                   struct Row99;
11240                   struct Row9;
11241                   struct Row10;"#},
11242        base_text,
11243        &mut cx,
11244    );
11245
11246    assert_hunk_revert(
11247        indoc! {r#"ˇstruct Row1.1;
11248                   struct Row1;
11249                   «ˇstr»uct Row22;
11250
11251                   struct ˇRow44;
11252                   struct Row5;
11253                   struct «Rˇ»ow66;ˇ
11254
11255                   «struˇ»ct Row88;
11256                   struct Row9;
11257                   struct Row1011;ˇ"#},
11258        vec![
11259            DiffHunkStatus::Modified,
11260            DiffHunkStatus::Modified,
11261            DiffHunkStatus::Modified,
11262            DiffHunkStatus::Modified,
11263            DiffHunkStatus::Modified,
11264            DiffHunkStatus::Modified,
11265        ],
11266        indoc! {r#"struct Row;
11267                   ˇstruct Row1;
11268                   struct Row2;
11269                   ˇ
11270                   struct Row4;
11271                   ˇstruct Row5;
11272                   struct Row6;
11273                   ˇ
11274                   struct Row8;
11275                   ˇstruct Row9;
11276                   struct Row10;ˇ"#},
11277        base_text,
11278        &mut cx,
11279    );
11280}
11281
11282#[gpui::test]
11283async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11284    init_test(cx, |_| {});
11285    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11286    let base_text = indoc! {r#"struct Row;
11287struct Row1;
11288struct Row2;
11289
11290struct Row4;
11291struct Row5;
11292struct Row6;
11293
11294struct Row8;
11295struct Row9;
11296struct Row10;"#};
11297
11298    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11299    assert_hunk_revert(
11300        indoc! {r#"struct Row;
11301                   struct Row2;
11302
11303                   ˇstruct Row4;
11304                   struct Row5;
11305                   struct Row6;
11306                   ˇ
11307                   struct Row8;
11308                   struct Row10;"#},
11309        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11310        indoc! {r#"struct Row;
11311                   struct Row2;
11312
11313                   ˇstruct Row4;
11314                   struct Row5;
11315                   struct Row6;
11316                   ˇ
11317                   struct Row8;
11318                   struct Row10;"#},
11319        base_text,
11320        &mut cx,
11321    );
11322    assert_hunk_revert(
11323        indoc! {r#"struct Row;
11324                   struct Row2;
11325
11326                   «ˇstruct Row4;
11327                   struct» Row5;
11328                   «struct Row6;
11329                   ˇ»
11330                   struct Row8;
11331                   struct Row10;"#},
11332        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11333        indoc! {r#"struct Row;
11334                   struct Row2;
11335
11336                   «ˇstruct Row4;
11337                   struct» Row5;
11338                   «struct Row6;
11339                   ˇ»
11340                   struct Row8;
11341                   struct Row10;"#},
11342        base_text,
11343        &mut cx,
11344    );
11345
11346    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11347    assert_hunk_revert(
11348        indoc! {r#"struct Row;
11349                   ˇstruct Row2;
11350
11351                   struct Row4;
11352                   struct Row5;
11353                   struct Row6;
11354
11355                   struct Row8;ˇ
11356                   struct Row10;"#},
11357        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11358        indoc! {r#"struct Row;
11359                   struct Row1;
11360                   ˇstruct Row2;
11361
11362                   struct Row4;
11363                   struct Row5;
11364                   struct Row6;
11365
11366                   struct Row8;ˇ
11367                   struct Row9;
11368                   struct Row10;"#},
11369        base_text,
11370        &mut cx,
11371    );
11372    assert_hunk_revert(
11373        indoc! {r#"struct Row;
11374                   struct Row2«ˇ;
11375                   struct Row4;
11376                   struct» Row5;
11377                   «struct Row6;
11378
11379                   struct Row8;ˇ»
11380                   struct Row10;"#},
11381        vec![
11382            DiffHunkStatus::Removed,
11383            DiffHunkStatus::Removed,
11384            DiffHunkStatus::Removed,
11385        ],
11386        indoc! {r#"struct Row;
11387                   struct Row1;
11388                   struct Row2«ˇ;
11389
11390                   struct Row4;
11391                   struct» Row5;
11392                   «struct Row6;
11393
11394                   struct Row8;ˇ»
11395                   struct Row9;
11396                   struct Row10;"#},
11397        base_text,
11398        &mut cx,
11399    );
11400}
11401
11402#[gpui::test]
11403async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11404    init_test(cx, |_| {});
11405
11406    let cols = 4;
11407    let rows = 10;
11408    let sample_text_1 = sample_text(rows, cols, 'a');
11409    assert_eq!(
11410        sample_text_1,
11411        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11412    );
11413    let sample_text_2 = sample_text(rows, cols, 'l');
11414    assert_eq!(
11415        sample_text_2,
11416        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11417    );
11418    let sample_text_3 = sample_text(rows, cols, 'v');
11419    assert_eq!(
11420        sample_text_3,
11421        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11422    );
11423
11424    fn diff_every_buffer_row(
11425        buffer: &Model<Buffer>,
11426        sample_text: String,
11427        cols: usize,
11428        cx: &mut gpui::TestAppContext,
11429    ) {
11430        // revert first character in each row, creating one large diff hunk per buffer
11431        let is_first_char = |offset: usize| offset % cols == 0;
11432        buffer.update(cx, |buffer, cx| {
11433            buffer.set_text(
11434                sample_text
11435                    .chars()
11436                    .enumerate()
11437                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11438                    .collect::<String>(),
11439                cx,
11440            );
11441            buffer.set_diff_base(Some(sample_text), cx);
11442        });
11443        cx.executor().run_until_parked();
11444    }
11445
11446    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11447    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11448
11449    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11450    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11451
11452    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11453    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11454
11455    let multibuffer = cx.new_model(|cx| {
11456        let mut multibuffer = MultiBuffer::new(ReadWrite);
11457        multibuffer.push_excerpts(
11458            buffer_1.clone(),
11459            [
11460                ExcerptRange {
11461                    context: Point::new(0, 0)..Point::new(3, 0),
11462                    primary: None,
11463                },
11464                ExcerptRange {
11465                    context: Point::new(5, 0)..Point::new(7, 0),
11466                    primary: None,
11467                },
11468                ExcerptRange {
11469                    context: Point::new(9, 0)..Point::new(10, 4),
11470                    primary: None,
11471                },
11472            ],
11473            cx,
11474        );
11475        multibuffer.push_excerpts(
11476            buffer_2.clone(),
11477            [
11478                ExcerptRange {
11479                    context: Point::new(0, 0)..Point::new(3, 0),
11480                    primary: None,
11481                },
11482                ExcerptRange {
11483                    context: Point::new(5, 0)..Point::new(7, 0),
11484                    primary: None,
11485                },
11486                ExcerptRange {
11487                    context: Point::new(9, 0)..Point::new(10, 4),
11488                    primary: None,
11489                },
11490            ],
11491            cx,
11492        );
11493        multibuffer.push_excerpts(
11494            buffer_3.clone(),
11495            [
11496                ExcerptRange {
11497                    context: Point::new(0, 0)..Point::new(3, 0),
11498                    primary: None,
11499                },
11500                ExcerptRange {
11501                    context: Point::new(5, 0)..Point::new(7, 0),
11502                    primary: None,
11503                },
11504                ExcerptRange {
11505                    context: Point::new(9, 0)..Point::new(10, 4),
11506                    primary: None,
11507                },
11508            ],
11509            cx,
11510        );
11511        multibuffer
11512    });
11513
11514    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11515    editor.update(cx, |editor, cx| {
11516        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");
11517        editor.select_all(&SelectAll, cx);
11518        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11519    });
11520    cx.executor().run_until_parked();
11521    // When all ranges are selected, all buffer hunks are reverted.
11522    editor.update(cx, |editor, cx| {
11523        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");
11524    });
11525    buffer_1.update(cx, |buffer, _| {
11526        assert_eq!(buffer.text(), sample_text_1);
11527    });
11528    buffer_2.update(cx, |buffer, _| {
11529        assert_eq!(buffer.text(), sample_text_2);
11530    });
11531    buffer_3.update(cx, |buffer, _| {
11532        assert_eq!(buffer.text(), sample_text_3);
11533    });
11534
11535    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11536    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11537    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11538    editor.update(cx, |editor, cx| {
11539        editor.change_selections(None, cx, |s| {
11540            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11541        });
11542        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11543    });
11544    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11545    // but not affect buffer_2 and its related excerpts.
11546    editor.update(cx, |editor, cx| {
11547        assert_eq!(
11548            editor.text(cx),
11549            "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"
11550        );
11551    });
11552    buffer_1.update(cx, |buffer, _| {
11553        assert_eq!(buffer.text(), sample_text_1);
11554    });
11555    buffer_2.update(cx, |buffer, _| {
11556        assert_eq!(
11557            buffer.text(),
11558            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11559        );
11560    });
11561    buffer_3.update(cx, |buffer, _| {
11562        assert_eq!(
11563            buffer.text(),
11564            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11565        );
11566    });
11567}
11568
11569#[gpui::test]
11570async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11571    init_test(cx, |_| {});
11572
11573    let cols = 4;
11574    let rows = 10;
11575    let sample_text_1 = sample_text(rows, cols, 'a');
11576    assert_eq!(
11577        sample_text_1,
11578        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11579    );
11580    let sample_text_2 = sample_text(rows, cols, 'l');
11581    assert_eq!(
11582        sample_text_2,
11583        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11584    );
11585    let sample_text_3 = sample_text(rows, cols, 'v');
11586    assert_eq!(
11587        sample_text_3,
11588        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11589    );
11590
11591    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11592    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11593    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11594
11595    let multi_buffer = cx.new_model(|cx| {
11596        let mut multibuffer = MultiBuffer::new(ReadWrite);
11597        multibuffer.push_excerpts(
11598            buffer_1.clone(),
11599            [
11600                ExcerptRange {
11601                    context: Point::new(0, 0)..Point::new(3, 0),
11602                    primary: None,
11603                },
11604                ExcerptRange {
11605                    context: Point::new(5, 0)..Point::new(7, 0),
11606                    primary: None,
11607                },
11608                ExcerptRange {
11609                    context: Point::new(9, 0)..Point::new(10, 4),
11610                    primary: None,
11611                },
11612            ],
11613            cx,
11614        );
11615        multibuffer.push_excerpts(
11616            buffer_2.clone(),
11617            [
11618                ExcerptRange {
11619                    context: Point::new(0, 0)..Point::new(3, 0),
11620                    primary: None,
11621                },
11622                ExcerptRange {
11623                    context: Point::new(5, 0)..Point::new(7, 0),
11624                    primary: None,
11625                },
11626                ExcerptRange {
11627                    context: Point::new(9, 0)..Point::new(10, 4),
11628                    primary: None,
11629                },
11630            ],
11631            cx,
11632        );
11633        multibuffer.push_excerpts(
11634            buffer_3.clone(),
11635            [
11636                ExcerptRange {
11637                    context: Point::new(0, 0)..Point::new(3, 0),
11638                    primary: None,
11639                },
11640                ExcerptRange {
11641                    context: Point::new(5, 0)..Point::new(7, 0),
11642                    primary: None,
11643                },
11644                ExcerptRange {
11645                    context: Point::new(9, 0)..Point::new(10, 4),
11646                    primary: None,
11647                },
11648            ],
11649            cx,
11650        );
11651        multibuffer
11652    });
11653
11654    let fs = FakeFs::new(cx.executor());
11655    fs.insert_tree(
11656        "/a",
11657        json!({
11658            "main.rs": sample_text_1,
11659            "other.rs": sample_text_2,
11660            "lib.rs": sample_text_3,
11661        }),
11662    )
11663    .await;
11664    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11665    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11666    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11667    let multi_buffer_editor = cx.new_view(|cx| {
11668        Editor::new(
11669            EditorMode::Full,
11670            multi_buffer,
11671            Some(project.clone()),
11672            true,
11673            cx,
11674        )
11675    });
11676    let multibuffer_item_id = workspace
11677        .update(cx, |workspace, cx| {
11678            assert!(
11679                workspace.active_item(cx).is_none(),
11680                "active item should be None before the first item is added"
11681            );
11682            workspace.add_item_to_active_pane(
11683                Box::new(multi_buffer_editor.clone()),
11684                None,
11685                true,
11686                cx,
11687            );
11688            let active_item = workspace
11689                .active_item(cx)
11690                .expect("should have an active item after adding the multi buffer");
11691            assert!(
11692                !active_item.is_singleton(cx),
11693                "A multi buffer was expected to active after adding"
11694            );
11695            active_item.item_id()
11696        })
11697        .unwrap();
11698    cx.executor().run_until_parked();
11699
11700    multi_buffer_editor.update(cx, |editor, cx| {
11701        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11702        editor.open_excerpts(&OpenExcerpts, cx);
11703    });
11704    cx.executor().run_until_parked();
11705    let first_item_id = workspace
11706        .update(cx, |workspace, cx| {
11707            let active_item = workspace
11708                .active_item(cx)
11709                .expect("should have an active item after navigating into the 1st buffer");
11710            let first_item_id = active_item.item_id();
11711            assert_ne!(
11712                first_item_id, multibuffer_item_id,
11713                "Should navigate into the 1st buffer and activate it"
11714            );
11715            assert!(
11716                active_item.is_singleton(cx),
11717                "New active item should be a singleton buffer"
11718            );
11719            assert_eq!(
11720                active_item
11721                    .act_as::<Editor>(cx)
11722                    .expect("should have navigated into an editor for the 1st buffer")
11723                    .read(cx)
11724                    .text(cx),
11725                sample_text_1
11726            );
11727
11728            workspace
11729                .go_back(workspace.active_pane().downgrade(), cx)
11730                .detach_and_log_err(cx);
11731
11732            first_item_id
11733        })
11734        .unwrap();
11735    cx.executor().run_until_parked();
11736    workspace
11737        .update(cx, |workspace, cx| {
11738            let active_item = workspace
11739                .active_item(cx)
11740                .expect("should have an active item after navigating back");
11741            assert_eq!(
11742                active_item.item_id(),
11743                multibuffer_item_id,
11744                "Should navigate back to the multi buffer"
11745            );
11746            assert!(!active_item.is_singleton(cx));
11747        })
11748        .unwrap();
11749
11750    multi_buffer_editor.update(cx, |editor, cx| {
11751        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11752            s.select_ranges(Some(39..40))
11753        });
11754        editor.open_excerpts(&OpenExcerpts, cx);
11755    });
11756    cx.executor().run_until_parked();
11757    let second_item_id = workspace
11758        .update(cx, |workspace, cx| {
11759            let active_item = workspace
11760                .active_item(cx)
11761                .expect("should have an active item after navigating into the 2nd buffer");
11762            let second_item_id = active_item.item_id();
11763            assert_ne!(
11764                second_item_id, multibuffer_item_id,
11765                "Should navigate away from the multibuffer"
11766            );
11767            assert_ne!(
11768                second_item_id, first_item_id,
11769                "Should navigate into the 2nd buffer and activate it"
11770            );
11771            assert!(
11772                active_item.is_singleton(cx),
11773                "New active item should be a singleton buffer"
11774            );
11775            assert_eq!(
11776                active_item
11777                    .act_as::<Editor>(cx)
11778                    .expect("should have navigated into an editor")
11779                    .read(cx)
11780                    .text(cx),
11781                sample_text_2
11782            );
11783
11784            workspace
11785                .go_back(workspace.active_pane().downgrade(), cx)
11786                .detach_and_log_err(cx);
11787
11788            second_item_id
11789        })
11790        .unwrap();
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 back from the 2nd buffer");
11797            assert_eq!(
11798                active_item.item_id(),
11799                multibuffer_item_id,
11800                "Should navigate back from the 2nd buffer to the multi buffer"
11801            );
11802            assert!(!active_item.is_singleton(cx));
11803        })
11804        .unwrap();
11805
11806    multi_buffer_editor.update(cx, |editor, cx| {
11807        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11808            s.select_ranges(Some(70..70))
11809        });
11810        editor.open_excerpts(&OpenExcerpts, cx);
11811    });
11812    cx.executor().run_until_parked();
11813    workspace
11814        .update(cx, |workspace, cx| {
11815            let active_item = workspace
11816                .active_item(cx)
11817                .expect("should have an active item after navigating into the 3rd buffer");
11818            let third_item_id = active_item.item_id();
11819            assert_ne!(
11820                third_item_id, multibuffer_item_id,
11821                "Should navigate into the 3rd buffer and activate it"
11822            );
11823            assert_ne!(third_item_id, first_item_id);
11824            assert_ne!(third_item_id, second_item_id);
11825            assert!(
11826                active_item.is_singleton(cx),
11827                "New active item should be a singleton buffer"
11828            );
11829            assert_eq!(
11830                active_item
11831                    .act_as::<Editor>(cx)
11832                    .expect("should have navigated into an editor")
11833                    .read(cx)
11834                    .text(cx),
11835                sample_text_3
11836            );
11837
11838            workspace
11839                .go_back(workspace.active_pane().downgrade(), cx)
11840                .detach_and_log_err(cx);
11841        })
11842        .unwrap();
11843    cx.executor().run_until_parked();
11844    workspace
11845        .update(cx, |workspace, cx| {
11846            let active_item = workspace
11847                .active_item(cx)
11848                .expect("should have an active item after navigating back from the 3rd buffer");
11849            assert_eq!(
11850                active_item.item_id(),
11851                multibuffer_item_id,
11852                "Should navigate back from the 3rd buffer to the multi buffer"
11853            );
11854            assert!(!active_item.is_singleton(cx));
11855        })
11856        .unwrap();
11857}
11858
11859#[gpui::test]
11860async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11861    init_test(cx, |_| {});
11862
11863    let mut cx = EditorTestContext::new(cx).await;
11864
11865    let diff_base = r#"
11866        use some::mod;
11867
11868        const A: u32 = 42;
11869
11870        fn main() {
11871            println!("hello");
11872
11873            println!("world");
11874        }
11875        "#
11876    .unindent();
11877
11878    cx.set_state(
11879        &r#"
11880        use some::modified;
11881
11882        ˇ
11883        fn main() {
11884            println!("hello there");
11885
11886            println!("around the");
11887            println!("world");
11888        }
11889        "#
11890        .unindent(),
11891    );
11892
11893    cx.set_diff_base(Some(&diff_base));
11894    executor.run_until_parked();
11895
11896    cx.update_editor(|editor, cx| {
11897        editor.go_to_next_hunk(&GoToHunk, cx);
11898        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11899    });
11900    executor.run_until_parked();
11901    cx.assert_diff_hunks(
11902        r#"
11903          use some::modified;
11904
11905
11906          fn main() {
11907        -     println!("hello");
11908        +     println!("hello there");
11909
11910              println!("around the");
11911              println!("world");
11912          }
11913        "#
11914        .unindent(),
11915    );
11916
11917    cx.update_editor(|editor, cx| {
11918        for _ in 0..3 {
11919            editor.go_to_next_hunk(&GoToHunk, cx);
11920            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11921        }
11922    });
11923    executor.run_until_parked();
11924    cx.assert_editor_state(
11925        &r#"
11926        use some::modified;
11927
11928        ˇ
11929        fn main() {
11930            println!("hello there");
11931
11932            println!("around the");
11933            println!("world");
11934        }
11935        "#
11936        .unindent(),
11937    );
11938
11939    cx.assert_diff_hunks(
11940        r#"
11941        - use some::mod;
11942        + use some::modified;
11943
11944        - const A: u32 = 42;
11945
11946          fn main() {
11947        -     println!("hello");
11948        +     println!("hello there");
11949
11950        +     println!("around the");
11951              println!("world");
11952          }
11953        "#
11954        .unindent(),
11955    );
11956
11957    cx.update_editor(|editor, cx| {
11958        editor.cancel(&Cancel, cx);
11959    });
11960
11961    cx.assert_diff_hunks(
11962        r#"
11963          use some::modified;
11964
11965
11966          fn main() {
11967              println!("hello there");
11968
11969              println!("around the");
11970              println!("world");
11971          }
11972        "#
11973        .unindent(),
11974    );
11975}
11976
11977#[gpui::test]
11978async fn test_diff_base_change_with_expanded_diff_hunks(
11979    executor: BackgroundExecutor,
11980    cx: &mut gpui::TestAppContext,
11981) {
11982    init_test(cx, |_| {});
11983
11984    let mut cx = EditorTestContext::new(cx).await;
11985
11986    let diff_base = r#"
11987        use some::mod1;
11988        use some::mod2;
11989
11990        const A: u32 = 42;
11991        const B: u32 = 42;
11992        const C: u32 = 42;
11993
11994        fn main() {
11995            println!("hello");
11996
11997            println!("world");
11998        }
11999        "#
12000    .unindent();
12001
12002    cx.set_state(
12003        &r#"
12004        use some::mod2;
12005
12006        const A: u32 = 42;
12007        const C: u32 = 42;
12008
12009        fn main(ˇ) {
12010            //println!("hello");
12011
12012            println!("world");
12013            //
12014            //
12015        }
12016        "#
12017        .unindent(),
12018    );
12019
12020    cx.set_diff_base(Some(&diff_base));
12021    executor.run_until_parked();
12022
12023    cx.update_editor(|editor, cx| {
12024        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12025    });
12026    executor.run_until_parked();
12027    cx.assert_diff_hunks(
12028        r#"
12029        - use some::mod1;
12030          use some::mod2;
12031
12032          const A: u32 = 42;
12033        - const B: u32 = 42;
12034          const C: u32 = 42;
12035
12036          fn main() {
12037        -     println!("hello");
12038        +     //println!("hello");
12039
12040              println!("world");
12041        +     //
12042        +     //
12043          }
12044        "#
12045        .unindent(),
12046    );
12047
12048    cx.set_diff_base(Some("new diff base!"));
12049    executor.run_until_parked();
12050    cx.assert_diff_hunks(
12051        r#"
12052          use some::mod2;
12053
12054          const A: u32 = 42;
12055          const C: u32 = 42;
12056
12057          fn main() {
12058              //println!("hello");
12059
12060              println!("world");
12061              //
12062              //
12063          }
12064        "#
12065        .unindent(),
12066    );
12067
12068    cx.update_editor(|editor, cx| {
12069        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12070    });
12071    executor.run_until_parked();
12072    cx.assert_diff_hunks(
12073        r#"
12074        - new diff base!
12075        + use some::mod2;
12076        +
12077        + const A: u32 = 42;
12078        + const C: u32 = 42;
12079        +
12080        + fn main() {
12081        +     //println!("hello");
12082        +
12083        +     println!("world");
12084        +     //
12085        +     //
12086        + }
12087        "#
12088        .unindent(),
12089    );
12090}
12091
12092#[gpui::test]
12093async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12094    init_test(cx, |_| {});
12095
12096    let mut cx = EditorTestContext::new(cx).await;
12097
12098    let diff_base = r#"
12099        use some::mod1;
12100        use some::mod2;
12101
12102        const A: u32 = 42;
12103        const B: u32 = 42;
12104        const C: u32 = 42;
12105
12106        fn main() {
12107            println!("hello");
12108
12109            println!("world");
12110        }
12111
12112        fn another() {
12113            println!("another");
12114        }
12115
12116        fn another2() {
12117            println!("another2");
12118        }
12119        "#
12120    .unindent();
12121
12122    cx.set_state(
12123        &r#"
12124        «use some::mod2;
12125
12126        const A: u32 = 42;
12127        const C: u32 = 42;
12128
12129        fn main() {
12130            //println!("hello");
12131
12132            println!("world");
12133            //
12134            //ˇ»
12135        }
12136
12137        fn another() {
12138            println!("another");
12139            println!("another");
12140        }
12141
12142            println!("another2");
12143        }
12144        "#
12145        .unindent(),
12146    );
12147
12148    cx.set_diff_base(Some(&diff_base));
12149    executor.run_until_parked();
12150
12151    cx.update_editor(|editor, cx| {
12152        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12153    });
12154    executor.run_until_parked();
12155
12156    cx.assert_diff_hunks(
12157        r#"
12158        - use some::mod1;
12159          use some::mod2;
12160
12161          const A: u32 = 42;
12162        - const B: u32 = 42;
12163          const C: u32 = 42;
12164
12165          fn main() {
12166        -     println!("hello");
12167        +     //println!("hello");
12168
12169              println!("world");
12170        +     //
12171        +     //
12172          }
12173
12174          fn another() {
12175              println!("another");
12176        +     println!("another");
12177          }
12178
12179        - fn another2() {
12180              println!("another2");
12181          }
12182        "#
12183        .unindent(),
12184    );
12185
12186    // Fold across some of the diff hunks. They should no longer appear expanded.
12187    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12188    cx.executor().run_until_parked();
12189
12190    // Hunks are not shown if their position is within a fold
12191    cx.assert_diff_hunks(
12192        r#"
12193          use some::mod2;
12194
12195          const A: u32 = 42;
12196          const C: u32 = 42;
12197
12198          fn main() {
12199              //println!("hello");
12200
12201              println!("world");
12202              //
12203              //
12204          }
12205
12206          fn another() {
12207              println!("another");
12208        +     println!("another");
12209          }
12210
12211        - fn another2() {
12212              println!("another2");
12213          }
12214        "#
12215        .unindent(),
12216    );
12217
12218    cx.update_editor(|editor, cx| {
12219        editor.select_all(&SelectAll, cx);
12220        editor.unfold_lines(&UnfoldLines, cx);
12221    });
12222    cx.executor().run_until_parked();
12223
12224    // The deletions reappear when unfolding.
12225    cx.assert_diff_hunks(
12226        r#"
12227        - use some::mod1;
12228          use some::mod2;
12229
12230          const A: u32 = 42;
12231        - const B: u32 = 42;
12232          const C: u32 = 42;
12233
12234          fn main() {
12235        -     println!("hello");
12236        +     //println!("hello");
12237
12238              println!("world");
12239        +     //
12240        +     //
12241          }
12242
12243          fn another() {
12244              println!("another");
12245        +     println!("another");
12246          }
12247
12248        - fn another2() {
12249              println!("another2");
12250          }
12251        "#
12252        .unindent(),
12253    );
12254}
12255
12256#[gpui::test]
12257async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12258    init_test(cx, |_| {});
12259
12260    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12261    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12262    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12263    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12264    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12265    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12266
12267    let buffer_1 = cx.new_model(|cx| {
12268        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
12269        buffer.set_diff_base(Some(file_1_old.into()), cx);
12270        buffer
12271    });
12272    let buffer_2 = cx.new_model(|cx| {
12273        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
12274        buffer.set_diff_base(Some(file_2_old.into()), cx);
12275        buffer
12276    });
12277    let buffer_3 = cx.new_model(|cx| {
12278        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
12279        buffer.set_diff_base(Some(file_3_old.into()), cx);
12280        buffer
12281    });
12282
12283    let multi_buffer = cx.new_model(|cx| {
12284        let mut multibuffer = MultiBuffer::new(ReadWrite);
12285        multibuffer.push_excerpts(
12286            buffer_1.clone(),
12287            [
12288                ExcerptRange {
12289                    context: Point::new(0, 0)..Point::new(3, 0),
12290                    primary: None,
12291                },
12292                ExcerptRange {
12293                    context: Point::new(5, 0)..Point::new(7, 0),
12294                    primary: None,
12295                },
12296                ExcerptRange {
12297                    context: Point::new(9, 0)..Point::new(10, 3),
12298                    primary: None,
12299                },
12300            ],
12301            cx,
12302        );
12303        multibuffer.push_excerpts(
12304            buffer_2.clone(),
12305            [
12306                ExcerptRange {
12307                    context: Point::new(0, 0)..Point::new(3, 0),
12308                    primary: None,
12309                },
12310                ExcerptRange {
12311                    context: Point::new(5, 0)..Point::new(7, 0),
12312                    primary: None,
12313                },
12314                ExcerptRange {
12315                    context: Point::new(9, 0)..Point::new(10, 3),
12316                    primary: None,
12317                },
12318            ],
12319            cx,
12320        );
12321        multibuffer.push_excerpts(
12322            buffer_3.clone(),
12323            [
12324                ExcerptRange {
12325                    context: Point::new(0, 0)..Point::new(3, 0),
12326                    primary: None,
12327                },
12328                ExcerptRange {
12329                    context: Point::new(5, 0)..Point::new(7, 0),
12330                    primary: None,
12331                },
12332                ExcerptRange {
12333                    context: Point::new(9, 0)..Point::new(10, 3),
12334                    primary: None,
12335                },
12336            ],
12337            cx,
12338        );
12339        multibuffer
12340    });
12341
12342    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12343    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12344    cx.run_until_parked();
12345
12346    cx.assert_editor_state(
12347        &"
12348            ˇaaa
12349            ccc
12350            ddd
12351
12352            ggg
12353            hhh
12354
12355
12356            lll
12357            mmm
12358            NNN
12359
12360            qqq
12361            rrr
12362
12363            uuu
12364            111
12365            222
12366            333
12367
12368            666
12369            777
12370
12371            000
12372            !!!"
12373        .unindent(),
12374    );
12375
12376    cx.update_editor(|editor, cx| {
12377        editor.select_all(&SelectAll, cx);
12378        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12379    });
12380    cx.executor().run_until_parked();
12381
12382    cx.assert_diff_hunks(
12383        "
12384            aaa
12385          - bbb
12386            ccc
12387            ddd
12388
12389            ggg
12390            hhh
12391
12392
12393            lll
12394            mmm
12395          - nnn
12396          + NNN
12397
12398            qqq
12399            rrr
12400
12401            uuu
12402            111
12403            222
12404            333
12405
12406          + 666
12407            777
12408
12409            000
12410            !!!"
12411        .unindent(),
12412    );
12413}
12414
12415#[gpui::test]
12416async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12417    init_test(cx, |_| {});
12418
12419    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12420    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12421
12422    let buffer = cx.new_model(|cx| {
12423        let mut buffer = Buffer::local(text.to_string(), cx);
12424        buffer.set_diff_base(Some(base.into()), cx);
12425        buffer
12426    });
12427
12428    let multi_buffer = cx.new_model(|cx| {
12429        let mut multibuffer = MultiBuffer::new(ReadWrite);
12430        multibuffer.push_excerpts(
12431            buffer.clone(),
12432            [
12433                ExcerptRange {
12434                    context: Point::new(0, 0)..Point::new(2, 0),
12435                    primary: None,
12436                },
12437                ExcerptRange {
12438                    context: Point::new(5, 0)..Point::new(7, 0),
12439                    primary: None,
12440                },
12441            ],
12442            cx,
12443        );
12444        multibuffer
12445    });
12446
12447    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12448    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12449    cx.run_until_parked();
12450
12451    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12452    cx.executor().run_until_parked();
12453
12454    cx.assert_diff_hunks(
12455        "
12456            aaa
12457          - bbb
12458          + BBB
12459
12460          - ddd
12461          - eee
12462          + EEE
12463            fff
12464        "
12465        .unindent(),
12466    );
12467}
12468
12469#[gpui::test]
12470async fn test_edits_around_expanded_insertion_hunks(
12471    executor: BackgroundExecutor,
12472    cx: &mut gpui::TestAppContext,
12473) {
12474    init_test(cx, |_| {});
12475
12476    let mut cx = EditorTestContext::new(cx).await;
12477
12478    let diff_base = r#"
12479        use some::mod1;
12480        use some::mod2;
12481
12482        const A: u32 = 42;
12483
12484        fn main() {
12485            println!("hello");
12486
12487            println!("world");
12488        }
12489        "#
12490    .unindent();
12491    executor.run_until_parked();
12492    cx.set_state(
12493        &r#"
12494        use some::mod1;
12495        use some::mod2;
12496
12497        const A: u32 = 42;
12498        const B: u32 = 42;
12499        const C: u32 = 42;
12500        ˇ
12501
12502        fn main() {
12503            println!("hello");
12504
12505            println!("world");
12506        }
12507        "#
12508        .unindent(),
12509    );
12510
12511    cx.set_diff_base(Some(&diff_base));
12512    executor.run_until_parked();
12513
12514    cx.update_editor(|editor, cx| {
12515        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12516    });
12517    executor.run_until_parked();
12518
12519    cx.assert_diff_hunks(
12520        r#"
12521        use some::mod1;
12522        use some::mod2;
12523
12524        const A: u32 = 42;
12525      + const B: u32 = 42;
12526      + const C: u32 = 42;
12527      +
12528
12529        fn main() {
12530            println!("hello");
12531
12532            println!("world");
12533        }
12534        "#
12535        .unindent(),
12536    );
12537
12538    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12539    executor.run_until_parked();
12540
12541    cx.assert_diff_hunks(
12542        r#"
12543        use some::mod1;
12544        use some::mod2;
12545
12546        const A: u32 = 42;
12547      + const B: u32 = 42;
12548      + const C: u32 = 42;
12549      + const D: u32 = 42;
12550      +
12551
12552        fn main() {
12553            println!("hello");
12554
12555            println!("world");
12556        }
12557        "#
12558        .unindent(),
12559    );
12560
12561    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12562    executor.run_until_parked();
12563
12564    cx.assert_diff_hunks(
12565        r#"
12566        use some::mod1;
12567        use some::mod2;
12568
12569        const A: u32 = 42;
12570      + const B: u32 = 42;
12571      + const C: u32 = 42;
12572      + const D: u32 = 42;
12573      + const E: u32 = 42;
12574      +
12575
12576        fn main() {
12577            println!("hello");
12578
12579            println!("world");
12580        }
12581        "#
12582        .unindent(),
12583    );
12584
12585    cx.update_editor(|editor, cx| {
12586        editor.delete_line(&DeleteLine, cx);
12587    });
12588    executor.run_until_parked();
12589
12590    cx.assert_diff_hunks(
12591        r#"
12592        use some::mod1;
12593        use some::mod2;
12594
12595        const A: u32 = 42;
12596      + const B: u32 = 42;
12597      + const C: u32 = 42;
12598      + const D: u32 = 42;
12599      + const E: u32 = 42;
12600
12601        fn main() {
12602            println!("hello");
12603
12604            println!("world");
12605        }
12606        "#
12607        .unindent(),
12608    );
12609
12610    cx.update_editor(|editor, cx| {
12611        editor.move_up(&MoveUp, cx);
12612        editor.delete_line(&DeleteLine, cx);
12613        editor.move_up(&MoveUp, cx);
12614        editor.delete_line(&DeleteLine, cx);
12615        editor.move_up(&MoveUp, cx);
12616        editor.delete_line(&DeleteLine, cx);
12617    });
12618    executor.run_until_parked();
12619    cx.assert_editor_state(
12620        &r#"
12621        use some::mod1;
12622        use some::mod2;
12623
12624        const A: u32 = 42;
12625        const B: u32 = 42;
12626        ˇ
12627        fn main() {
12628            println!("hello");
12629
12630            println!("world");
12631        }
12632        "#
12633        .unindent(),
12634    );
12635
12636    cx.assert_diff_hunks(
12637        r#"
12638        use some::mod1;
12639        use some::mod2;
12640
12641        const A: u32 = 42;
12642      + const B: u32 = 42;
12643
12644        fn main() {
12645            println!("hello");
12646
12647            println!("world");
12648        }
12649        "#
12650        .unindent(),
12651    );
12652
12653    cx.update_editor(|editor, cx| {
12654        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12655        editor.delete_line(&DeleteLine, cx);
12656    });
12657    executor.run_until_parked();
12658    cx.assert_diff_hunks(
12659        r#"
12660        use some::mod1;
12661      - use some::mod2;
12662      -
12663      - const A: u32 = 42;
12664
12665        fn main() {
12666            println!("hello");
12667
12668            println!("world");
12669        }
12670        "#
12671        .unindent(),
12672    );
12673}
12674
12675#[gpui::test]
12676async fn test_edits_around_expanded_deletion_hunks(
12677    executor: BackgroundExecutor,
12678    cx: &mut gpui::TestAppContext,
12679) {
12680    init_test(cx, |_| {});
12681
12682    let mut cx = EditorTestContext::new(cx).await;
12683
12684    let diff_base = r#"
12685        use some::mod1;
12686        use some::mod2;
12687
12688        const A: u32 = 42;
12689        const B: u32 = 42;
12690        const C: u32 = 42;
12691
12692
12693        fn main() {
12694            println!("hello");
12695
12696            println!("world");
12697        }
12698    "#
12699    .unindent();
12700    executor.run_until_parked();
12701    cx.set_state(
12702        &r#"
12703        use some::mod1;
12704        use some::mod2;
12705
12706        ˇconst B: u32 = 42;
12707        const C: u32 = 42;
12708
12709
12710        fn main() {
12711            println!("hello");
12712
12713            println!("world");
12714        }
12715        "#
12716        .unindent(),
12717    );
12718
12719    cx.set_diff_base(Some(&diff_base));
12720    executor.run_until_parked();
12721
12722    cx.update_editor(|editor, cx| {
12723        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12724    });
12725    executor.run_until_parked();
12726
12727    cx.assert_diff_hunks(
12728        r#"
12729        use some::mod1;
12730        use some::mod2;
12731
12732      - const A: u32 = 42;
12733        const B: u32 = 42;
12734        const C: u32 = 42;
12735
12736
12737        fn main() {
12738            println!("hello");
12739
12740            println!("world");
12741        }
12742        "#
12743        .unindent(),
12744    );
12745
12746    cx.update_editor(|editor, cx| {
12747        editor.delete_line(&DeleteLine, cx);
12748    });
12749    executor.run_until_parked();
12750    cx.assert_editor_state(
12751        &r#"
12752        use some::mod1;
12753        use some::mod2;
12754
12755        ˇconst C: u32 = 42;
12756
12757
12758        fn main() {
12759            println!("hello");
12760
12761            println!("world");
12762        }
12763        "#
12764        .unindent(),
12765    );
12766    cx.assert_diff_hunks(
12767        r#"
12768        use some::mod1;
12769        use some::mod2;
12770
12771      - const A: u32 = 42;
12772      - const B: u32 = 42;
12773        const C: u32 = 42;
12774
12775
12776        fn main() {
12777            println!("hello");
12778
12779            println!("world");
12780        }
12781        "#
12782        .unindent(),
12783    );
12784
12785    cx.update_editor(|editor, cx| {
12786        editor.delete_line(&DeleteLine, cx);
12787    });
12788    executor.run_until_parked();
12789    cx.assert_editor_state(
12790        &r#"
12791        use some::mod1;
12792        use some::mod2;
12793
12794        ˇ
12795
12796        fn main() {
12797            println!("hello");
12798
12799            println!("world");
12800        }
12801        "#
12802        .unindent(),
12803    );
12804    cx.assert_diff_hunks(
12805        r#"
12806        use some::mod1;
12807        use some::mod2;
12808
12809      - const A: u32 = 42;
12810      - const B: u32 = 42;
12811      - const C: u32 = 42;
12812
12813
12814        fn main() {
12815            println!("hello");
12816
12817            println!("world");
12818        }
12819        "#
12820        .unindent(),
12821    );
12822
12823    cx.update_editor(|editor, cx| {
12824        editor.handle_input("replacement", cx);
12825    });
12826    executor.run_until_parked();
12827    cx.assert_editor_state(
12828        &r#"
12829        use some::mod1;
12830        use some::mod2;
12831
12832        replacementˇ
12833
12834        fn main() {
12835            println!("hello");
12836
12837            println!("world");
12838        }
12839        "#
12840        .unindent(),
12841    );
12842    cx.assert_diff_hunks(
12843        r#"
12844        use some::mod1;
12845        use some::mod2;
12846
12847      - const A: u32 = 42;
12848      - const B: u32 = 42;
12849      - const C: u32 = 42;
12850      -
12851      + replacement
12852
12853        fn main() {
12854            println!("hello");
12855
12856            println!("world");
12857        }
12858        "#
12859        .unindent(),
12860    );
12861}
12862
12863#[gpui::test]
12864async fn test_edit_after_expanded_modification_hunk(
12865    executor: BackgroundExecutor,
12866    cx: &mut gpui::TestAppContext,
12867) {
12868    init_test(cx, |_| {});
12869
12870    let mut cx = EditorTestContext::new(cx).await;
12871
12872    let diff_base = r#"
12873        use some::mod1;
12874        use some::mod2;
12875
12876        const A: u32 = 42;
12877        const B: u32 = 42;
12878        const C: u32 = 42;
12879        const D: u32 = 42;
12880
12881
12882        fn main() {
12883            println!("hello");
12884
12885            println!("world");
12886        }"#
12887    .unindent();
12888
12889    cx.set_state(
12890        &r#"
12891        use some::mod1;
12892        use some::mod2;
12893
12894        const A: u32 = 42;
12895        const B: u32 = 42;
12896        const C: u32 = 43ˇ
12897        const D: u32 = 42;
12898
12899
12900        fn main() {
12901            println!("hello");
12902
12903            println!("world");
12904        }"#
12905        .unindent(),
12906    );
12907
12908    cx.set_diff_base(Some(&diff_base));
12909    executor.run_until_parked();
12910    cx.update_editor(|editor, cx| {
12911        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12912    });
12913    executor.run_until_parked();
12914
12915    cx.assert_diff_hunks(
12916        r#"
12917        use some::mod1;
12918        use some::mod2;
12919
12920        const A: u32 = 42;
12921        const B: u32 = 42;
12922      - const C: u32 = 42;
12923      + const C: u32 = 43
12924        const D: u32 = 42;
12925
12926
12927        fn main() {
12928            println!("hello");
12929
12930            println!("world");
12931        }"#
12932        .unindent(),
12933    );
12934
12935    cx.update_editor(|editor, cx| {
12936        editor.handle_input("\nnew_line\n", cx);
12937    });
12938    executor.run_until_parked();
12939
12940    cx.assert_diff_hunks(
12941        r#"
12942        use some::mod1;
12943        use some::mod2;
12944
12945        const A: u32 = 42;
12946        const B: u32 = 42;
12947      - const C: u32 = 42;
12948      + const C: u32 = 43
12949      + new_line
12950      +
12951        const D: u32 = 42;
12952
12953
12954        fn main() {
12955            println!("hello");
12956
12957            println!("world");
12958        }"#
12959        .unindent(),
12960    );
12961}
12962
12963async fn setup_indent_guides_editor(
12964    text: &str,
12965    cx: &mut gpui::TestAppContext,
12966) -> (BufferId, EditorTestContext) {
12967    init_test(cx, |_| {});
12968
12969    let mut cx = EditorTestContext::new(cx).await;
12970
12971    let buffer_id = cx.update_editor(|editor, cx| {
12972        editor.set_text(text, cx);
12973        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12974
12975        buffer_ids[0]
12976    });
12977
12978    (buffer_id, cx)
12979}
12980
12981fn assert_indent_guides(
12982    range: Range<u32>,
12983    expected: Vec<IndentGuide>,
12984    active_indices: Option<Vec<usize>>,
12985    cx: &mut EditorTestContext,
12986) {
12987    let indent_guides = cx.update_editor(|editor, cx| {
12988        let snapshot = editor.snapshot(cx).display_snapshot;
12989        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12990            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12991            true,
12992            &snapshot,
12993            cx,
12994        );
12995
12996        indent_guides.sort_by(|a, b| {
12997            a.depth.cmp(&b.depth).then(
12998                a.start_row
12999                    .cmp(&b.start_row)
13000                    .then(a.end_row.cmp(&b.end_row)),
13001            )
13002        });
13003        indent_guides
13004    });
13005
13006    if let Some(expected) = active_indices {
13007        let active_indices = cx.update_editor(|editor, cx| {
13008            let snapshot = editor.snapshot(cx).display_snapshot;
13009            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13010        });
13011
13012        assert_eq!(
13013            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13014            expected,
13015            "Active indent guide indices do not match"
13016        );
13017    }
13018
13019    let expected: Vec<_> = expected
13020        .into_iter()
13021        .map(|guide| MultiBufferIndentGuide {
13022            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13023            buffer: guide,
13024        })
13025        .collect();
13026
13027    assert_eq!(indent_guides, expected, "Indent guides do not match");
13028}
13029
13030fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13031    IndentGuide {
13032        buffer_id,
13033        start_row,
13034        end_row,
13035        depth,
13036        tab_size: 4,
13037        settings: IndentGuideSettings {
13038            enabled: true,
13039            line_width: 1,
13040            active_line_width: 1,
13041            ..Default::default()
13042        },
13043    }
13044}
13045
13046#[gpui::test]
13047async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13048    let (buffer_id, mut cx) = setup_indent_guides_editor(
13049        &"
13050    fn main() {
13051        let a = 1;
13052    }"
13053        .unindent(),
13054        cx,
13055    )
13056    .await;
13057
13058    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13059}
13060
13061#[gpui::test]
13062async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13063    let (buffer_id, mut cx) = setup_indent_guides_editor(
13064        &"
13065    fn main() {
13066        let a = 1;
13067        let b = 2;
13068    }"
13069        .unindent(),
13070        cx,
13071    )
13072    .await;
13073
13074    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13075}
13076
13077#[gpui::test]
13078async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13079    let (buffer_id, mut cx) = setup_indent_guides_editor(
13080        &"
13081    fn main() {
13082        let a = 1;
13083        if a == 3 {
13084            let b = 2;
13085        } else {
13086            let c = 3;
13087        }
13088    }"
13089        .unindent(),
13090        cx,
13091    )
13092    .await;
13093
13094    assert_indent_guides(
13095        0..8,
13096        vec![
13097            indent_guide(buffer_id, 1, 6, 0),
13098            indent_guide(buffer_id, 3, 3, 1),
13099            indent_guide(buffer_id, 5, 5, 1),
13100        ],
13101        None,
13102        &mut cx,
13103    );
13104}
13105
13106#[gpui::test]
13107async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13108    let (buffer_id, mut cx) = setup_indent_guides_editor(
13109        &"
13110    fn main() {
13111        let a = 1;
13112            let b = 2;
13113        let c = 3;
13114    }"
13115        .unindent(),
13116        cx,
13117    )
13118    .await;
13119
13120    assert_indent_guides(
13121        0..5,
13122        vec![
13123            indent_guide(buffer_id, 1, 3, 0),
13124            indent_guide(buffer_id, 2, 2, 1),
13125        ],
13126        None,
13127        &mut cx,
13128    );
13129}
13130
13131#[gpui::test]
13132async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13133    let (buffer_id, mut cx) = setup_indent_guides_editor(
13134        &"
13135        fn main() {
13136            let a = 1;
13137
13138            let c = 3;
13139        }"
13140        .unindent(),
13141        cx,
13142    )
13143    .await;
13144
13145    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13146}
13147
13148#[gpui::test]
13149async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13150    let (buffer_id, mut cx) = setup_indent_guides_editor(
13151        &"
13152        fn main() {
13153            let a = 1;
13154
13155            let c = 3;
13156
13157            if a == 3 {
13158                let b = 2;
13159            } else {
13160                let c = 3;
13161            }
13162        }"
13163        .unindent(),
13164        cx,
13165    )
13166    .await;
13167
13168    assert_indent_guides(
13169        0..11,
13170        vec![
13171            indent_guide(buffer_id, 1, 9, 0),
13172            indent_guide(buffer_id, 6, 6, 1),
13173            indent_guide(buffer_id, 8, 8, 1),
13174        ],
13175        None,
13176        &mut cx,
13177    );
13178}
13179
13180#[gpui::test]
13181async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13182    let (buffer_id, mut cx) = setup_indent_guides_editor(
13183        &"
13184        fn main() {
13185            let a = 1;
13186
13187            let c = 3;
13188
13189            if a == 3 {
13190                let b = 2;
13191            } else {
13192                let c = 3;
13193            }
13194        }"
13195        .unindent(),
13196        cx,
13197    )
13198    .await;
13199
13200    assert_indent_guides(
13201        1..11,
13202        vec![
13203            indent_guide(buffer_id, 1, 9, 0),
13204            indent_guide(buffer_id, 6, 6, 1),
13205            indent_guide(buffer_id, 8, 8, 1),
13206        ],
13207        None,
13208        &mut cx,
13209    );
13210}
13211
13212#[gpui::test]
13213async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13214    let (buffer_id, mut cx) = setup_indent_guides_editor(
13215        &"
13216        fn main() {
13217            let a = 1;
13218
13219            let c = 3;
13220
13221            if a == 3 {
13222                let b = 2;
13223            } else {
13224                let c = 3;
13225            }
13226        }"
13227        .unindent(),
13228        cx,
13229    )
13230    .await;
13231
13232    assert_indent_guides(
13233        1..10,
13234        vec![
13235            indent_guide(buffer_id, 1, 9, 0),
13236            indent_guide(buffer_id, 6, 6, 1),
13237            indent_guide(buffer_id, 8, 8, 1),
13238        ],
13239        None,
13240        &mut cx,
13241    );
13242}
13243
13244#[gpui::test]
13245async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13246    let (buffer_id, mut cx) = setup_indent_guides_editor(
13247        &"
13248        block1
13249            block2
13250                block3
13251                    block4
13252            block2
13253        block1
13254        block1"
13255            .unindent(),
13256        cx,
13257    )
13258    .await;
13259
13260    assert_indent_guides(
13261        1..10,
13262        vec![
13263            indent_guide(buffer_id, 1, 4, 0),
13264            indent_guide(buffer_id, 2, 3, 1),
13265            indent_guide(buffer_id, 3, 3, 2),
13266        ],
13267        None,
13268        &mut cx,
13269    );
13270}
13271
13272#[gpui::test]
13273async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13274    let (buffer_id, mut cx) = setup_indent_guides_editor(
13275        &"
13276        block1
13277            block2
13278                block3
13279
13280        block1
13281        block1"
13282            .unindent(),
13283        cx,
13284    )
13285    .await;
13286
13287    assert_indent_guides(
13288        0..6,
13289        vec![
13290            indent_guide(buffer_id, 1, 2, 0),
13291            indent_guide(buffer_id, 2, 2, 1),
13292        ],
13293        None,
13294        &mut cx,
13295    );
13296}
13297
13298#[gpui::test]
13299async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13300    let (buffer_id, mut cx) = setup_indent_guides_editor(
13301        &"
13302        block1
13303
13304
13305
13306            block2
13307        "
13308        .unindent(),
13309        cx,
13310    )
13311    .await;
13312
13313    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13314}
13315
13316#[gpui::test]
13317async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13318    let (buffer_id, mut cx) = setup_indent_guides_editor(
13319        &"
13320        def a:
13321        \tb = 3
13322        \tif True:
13323        \t\tc = 4
13324        \t\td = 5
13325        \tprint(b)
13326        "
13327        .unindent(),
13328        cx,
13329    )
13330    .await;
13331
13332    assert_indent_guides(
13333        0..6,
13334        vec![
13335            indent_guide(buffer_id, 1, 6, 0),
13336            indent_guide(buffer_id, 3, 4, 1),
13337        ],
13338        None,
13339        &mut cx,
13340    );
13341}
13342
13343#[gpui::test]
13344async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13345    let (buffer_id, mut cx) = setup_indent_guides_editor(
13346        &"
13347    fn main() {
13348        let a = 1;
13349    }"
13350        .unindent(),
13351        cx,
13352    )
13353    .await;
13354
13355    cx.update_editor(|editor, cx| {
13356        editor.change_selections(None, cx, |s| {
13357            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13358        });
13359    });
13360
13361    assert_indent_guides(
13362        0..3,
13363        vec![indent_guide(buffer_id, 1, 1, 0)],
13364        Some(vec![0]),
13365        &mut cx,
13366    );
13367}
13368
13369#[gpui::test]
13370async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13371    let (buffer_id, mut cx) = setup_indent_guides_editor(
13372        &"
13373    fn main() {
13374        if 1 == 2 {
13375            let a = 1;
13376        }
13377    }"
13378        .unindent(),
13379        cx,
13380    )
13381    .await;
13382
13383    cx.update_editor(|editor, cx| {
13384        editor.change_selections(None, cx, |s| {
13385            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13386        });
13387    });
13388
13389    assert_indent_guides(
13390        0..4,
13391        vec![
13392            indent_guide(buffer_id, 1, 3, 0),
13393            indent_guide(buffer_id, 2, 2, 1),
13394        ],
13395        Some(vec![1]),
13396        &mut cx,
13397    );
13398
13399    cx.update_editor(|editor, cx| {
13400        editor.change_selections(None, cx, |s| {
13401            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13402        });
13403    });
13404
13405    assert_indent_guides(
13406        0..4,
13407        vec![
13408            indent_guide(buffer_id, 1, 3, 0),
13409            indent_guide(buffer_id, 2, 2, 1),
13410        ],
13411        Some(vec![1]),
13412        &mut cx,
13413    );
13414
13415    cx.update_editor(|editor, cx| {
13416        editor.change_selections(None, cx, |s| {
13417            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13418        });
13419    });
13420
13421    assert_indent_guides(
13422        0..4,
13423        vec![
13424            indent_guide(buffer_id, 1, 3, 0),
13425            indent_guide(buffer_id, 2, 2, 1),
13426        ],
13427        Some(vec![0]),
13428        &mut cx,
13429    );
13430}
13431
13432#[gpui::test]
13433async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13434    let (buffer_id, mut cx) = setup_indent_guides_editor(
13435        &"
13436    fn main() {
13437        let a = 1;
13438
13439        let b = 2;
13440    }"
13441        .unindent(),
13442        cx,
13443    )
13444    .await;
13445
13446    cx.update_editor(|editor, cx| {
13447        editor.change_selections(None, cx, |s| {
13448            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13449        });
13450    });
13451
13452    assert_indent_guides(
13453        0..5,
13454        vec![indent_guide(buffer_id, 1, 3, 0)],
13455        Some(vec![0]),
13456        &mut cx,
13457    );
13458}
13459
13460#[gpui::test]
13461async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13462    let (buffer_id, mut cx) = setup_indent_guides_editor(
13463        &"
13464    def m:
13465        a = 1
13466        pass"
13467            .unindent(),
13468        cx,
13469    )
13470    .await;
13471
13472    cx.update_editor(|editor, cx| {
13473        editor.change_selections(None, cx, |s| {
13474            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13475        });
13476    });
13477
13478    assert_indent_guides(
13479        0..3,
13480        vec![indent_guide(buffer_id, 1, 2, 0)],
13481        Some(vec![0]),
13482        &mut cx,
13483    );
13484}
13485
13486#[gpui::test]
13487fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13488    init_test(cx, |_| {});
13489
13490    let editor = cx.add_window(|cx| {
13491        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13492        build_editor(buffer, cx)
13493    });
13494
13495    let render_args = Arc::new(Mutex::new(None));
13496    let snapshot = editor
13497        .update(cx, |editor, cx| {
13498            let snapshot = editor.buffer().read(cx).snapshot(cx);
13499            let range =
13500                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13501
13502            struct RenderArgs {
13503                row: MultiBufferRow,
13504                folded: bool,
13505                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13506            }
13507
13508            let crease = Crease::inline(
13509                range,
13510                FoldPlaceholder::test(),
13511                {
13512                    let toggle_callback = render_args.clone();
13513                    move |row, folded, callback, _cx| {
13514                        *toggle_callback.lock() = Some(RenderArgs {
13515                            row,
13516                            folded,
13517                            callback,
13518                        });
13519                        div()
13520                    }
13521                },
13522                |_row, _folded, _cx| div(),
13523            );
13524
13525            editor.insert_creases(Some(crease), cx);
13526            let snapshot = editor.snapshot(cx);
13527            let _div =
13528                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13529            snapshot
13530        })
13531        .unwrap();
13532
13533    let render_args = render_args.lock().take().unwrap();
13534    assert_eq!(render_args.row, MultiBufferRow(1));
13535    assert!(!render_args.folded);
13536    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13537
13538    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13539        .unwrap();
13540    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13541    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13542
13543    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13544        .unwrap();
13545    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13546    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13547}
13548
13549#[gpui::test]
13550async fn test_input_text(cx: &mut gpui::TestAppContext) {
13551    init_test(cx, |_| {});
13552    let mut cx = EditorTestContext::new(cx).await;
13553
13554    cx.set_state(
13555        &r#"ˇone
13556        two
13557
13558        three
13559        fourˇ
13560        five
13561
13562        siˇx"#
13563            .unindent(),
13564    );
13565
13566    cx.dispatch_action(HandleInput(String::new()));
13567    cx.assert_editor_state(
13568        &r#"ˇone
13569        two
13570
13571        three
13572        fourˇ
13573        five
13574
13575        siˇx"#
13576            .unindent(),
13577    );
13578
13579    cx.dispatch_action(HandleInput("AAAA".to_string()));
13580    cx.assert_editor_state(
13581        &r#"AAAAˇone
13582        two
13583
13584        three
13585        fourAAAAˇ
13586        five
13587
13588        siAAAAˇx"#
13589            .unindent(),
13590    );
13591}
13592
13593#[gpui::test]
13594async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13595    init_test(cx, |_| {});
13596
13597    let mut cx = EditorTestContext::new(cx).await;
13598    cx.set_state(
13599        r#"let foo = 1;
13600let foo = 2;
13601let foo = 3;
13602let fooˇ = 4;
13603let foo = 5;
13604let foo = 6;
13605let foo = 7;
13606let foo = 8;
13607let foo = 9;
13608let foo = 10;
13609let foo = 11;
13610let foo = 12;
13611let foo = 13;
13612let foo = 14;
13613let foo = 15;"#,
13614    );
13615
13616    cx.update_editor(|e, cx| {
13617        assert_eq!(
13618            e.next_scroll_position,
13619            NextScrollCursorCenterTopBottom::Center,
13620            "Default next scroll direction is center",
13621        );
13622
13623        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13624        assert_eq!(
13625            e.next_scroll_position,
13626            NextScrollCursorCenterTopBottom::Top,
13627            "After center, next scroll direction should be top",
13628        );
13629
13630        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13631        assert_eq!(
13632            e.next_scroll_position,
13633            NextScrollCursorCenterTopBottom::Bottom,
13634            "After top, next scroll direction should be bottom",
13635        );
13636
13637        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13638        assert_eq!(
13639            e.next_scroll_position,
13640            NextScrollCursorCenterTopBottom::Center,
13641            "After bottom, scrolling should start over",
13642        );
13643
13644        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13645        assert_eq!(
13646            e.next_scroll_position,
13647            NextScrollCursorCenterTopBottom::Top,
13648            "Scrolling continues if retriggered fast enough"
13649        );
13650    });
13651
13652    cx.executor()
13653        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13654    cx.executor().run_until_parked();
13655    cx.update_editor(|e, _| {
13656        assert_eq!(
13657            e.next_scroll_position,
13658            NextScrollCursorCenterTopBottom::Center,
13659            "If scrolling is not triggered fast enough, it should reset"
13660        );
13661    });
13662}
13663
13664#[gpui::test]
13665async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13666    init_test(cx, |_| {});
13667    let mut cx = EditorLspTestContext::new_rust(
13668        lsp::ServerCapabilities {
13669            definition_provider: Some(lsp::OneOf::Left(true)),
13670            references_provider: Some(lsp::OneOf::Left(true)),
13671            ..lsp::ServerCapabilities::default()
13672        },
13673        cx,
13674    )
13675    .await;
13676
13677    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13678        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13679            move |params, _| async move {
13680                if empty_go_to_definition {
13681                    Ok(None)
13682                } else {
13683                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13684                        uri: params.text_document_position_params.text_document.uri,
13685                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13686                    })))
13687                }
13688            },
13689        );
13690        let references =
13691            cx.lsp
13692                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13693                    Ok(Some(vec![lsp::Location {
13694                        uri: params.text_document_position.text_document.uri,
13695                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13696                    }]))
13697                });
13698        (go_to_definition, references)
13699    };
13700
13701    cx.set_state(
13702        &r#"fn one() {
13703            let mut a = ˇtwo();
13704        }
13705
13706        fn two() {}"#
13707            .unindent(),
13708    );
13709    set_up_lsp_handlers(false, &mut cx);
13710    let navigated = cx
13711        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13712        .await
13713        .expect("Failed to navigate to definition");
13714    assert_eq!(
13715        navigated,
13716        Navigated::Yes,
13717        "Should have navigated to definition from the GetDefinition response"
13718    );
13719    cx.assert_editor_state(
13720        &r#"fn one() {
13721            let mut a = two();
13722        }
13723
13724        fn «twoˇ»() {}"#
13725            .unindent(),
13726    );
13727
13728    let editors = cx.update_workspace(|workspace, cx| {
13729        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13730    });
13731    cx.update_editor(|_, test_editor_cx| {
13732        assert_eq!(
13733            editors.len(),
13734            1,
13735            "Initially, only one, test, editor should be open in the workspace"
13736        );
13737        assert_eq!(
13738            test_editor_cx.view(),
13739            editors.last().expect("Asserted len is 1")
13740        );
13741    });
13742
13743    set_up_lsp_handlers(true, &mut cx);
13744    let navigated = cx
13745        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13746        .await
13747        .expect("Failed to navigate to lookup references");
13748    assert_eq!(
13749        navigated,
13750        Navigated::Yes,
13751        "Should have navigated to references as a fallback after empty GoToDefinition response"
13752    );
13753    // We should not change the selections in the existing file,
13754    // if opening another milti buffer with the references
13755    cx.assert_editor_state(
13756        &r#"fn one() {
13757            let mut a = two();
13758        }
13759
13760        fn «twoˇ»() {}"#
13761            .unindent(),
13762    );
13763    let editors = cx.update_workspace(|workspace, cx| {
13764        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13765    });
13766    cx.update_editor(|_, test_editor_cx| {
13767        assert_eq!(
13768            editors.len(),
13769            2,
13770            "After falling back to references search, we open a new editor with the results"
13771        );
13772        let references_fallback_text = editors
13773            .into_iter()
13774            .find(|new_editor| new_editor != test_editor_cx.view())
13775            .expect("Should have one non-test editor now")
13776            .read(test_editor_cx)
13777            .text(test_editor_cx);
13778        assert_eq!(
13779            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13780            "Should use the range from the references response and not the GoToDefinition one"
13781        );
13782    });
13783}
13784
13785#[gpui::test]
13786async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13787    init_test(cx, |_| {});
13788
13789    let language = Arc::new(Language::new(
13790        LanguageConfig::default(),
13791        Some(tree_sitter_rust::LANGUAGE.into()),
13792    ));
13793
13794    let text = r#"
13795        #[cfg(test)]
13796        mod tests() {
13797            #[test]
13798            fn runnable_1() {
13799                let a = 1;
13800            }
13801
13802            #[test]
13803            fn runnable_2() {
13804                let a = 1;
13805                let b = 2;
13806            }
13807        }
13808    "#
13809    .unindent();
13810
13811    let fs = FakeFs::new(cx.executor());
13812    fs.insert_file("/file.rs", Default::default()).await;
13813
13814    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13815    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13816    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13817    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13818    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13819
13820    let editor = cx.new_view(|cx| {
13821        Editor::new(
13822            EditorMode::Full,
13823            multi_buffer,
13824            Some(project.clone()),
13825            true,
13826            cx,
13827        )
13828    });
13829
13830    editor.update(cx, |editor, cx| {
13831        editor.tasks.insert(
13832            (buffer.read(cx).remote_id(), 3),
13833            RunnableTasks {
13834                templates: vec![],
13835                offset: MultiBufferOffset(43),
13836                column: 0,
13837                extra_variables: HashMap::default(),
13838                context_range: BufferOffset(43)..BufferOffset(85),
13839            },
13840        );
13841        editor.tasks.insert(
13842            (buffer.read(cx).remote_id(), 8),
13843            RunnableTasks {
13844                templates: vec![],
13845                offset: MultiBufferOffset(86),
13846                column: 0,
13847                extra_variables: HashMap::default(),
13848                context_range: BufferOffset(86)..BufferOffset(191),
13849            },
13850        );
13851
13852        // Test finding task when cursor is inside function body
13853        editor.change_selections(None, cx, |s| {
13854            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13855        });
13856        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13857        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13858
13859        // Test finding task when cursor is on function name
13860        editor.change_selections(None, cx, |s| {
13861            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13862        });
13863        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13864        assert_eq!(row, 8, "Should find task when cursor is on function name");
13865    });
13866}
13867
13868fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13869    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13870    point..point
13871}
13872
13873fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13874    let (text, ranges) = marked_text_ranges(marked_text, true);
13875    assert_eq!(view.text(cx), text);
13876    assert_eq!(
13877        view.selections.ranges(cx),
13878        ranges,
13879        "Assert selections are {}",
13880        marked_text
13881    );
13882}
13883
13884pub fn handle_signature_help_request(
13885    cx: &mut EditorLspTestContext,
13886    mocked_response: lsp::SignatureHelp,
13887) -> impl Future<Output = ()> {
13888    let mut request =
13889        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13890            let mocked_response = mocked_response.clone();
13891            async move { Ok(Some(mocked_response)) }
13892        });
13893
13894    async move {
13895        request.next().await;
13896    }
13897}
13898
13899/// Handle completion request passing a marked string specifying where the completion
13900/// should be triggered from using '|' character, what range should be replaced, and what completions
13901/// should be returned using '<' and '>' to delimit the range
13902pub fn handle_completion_request(
13903    cx: &mut EditorLspTestContext,
13904    marked_string: &str,
13905    completions: Vec<&'static str>,
13906    counter: Arc<AtomicUsize>,
13907) -> impl Future<Output = ()> {
13908    let complete_from_marker: TextRangeMarker = '|'.into();
13909    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13910    let (_, mut marked_ranges) = marked_text_ranges_by(
13911        marked_string,
13912        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13913    );
13914
13915    let complete_from_position =
13916        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13917    let replace_range =
13918        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13919
13920    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13921        let completions = completions.clone();
13922        counter.fetch_add(1, atomic::Ordering::Release);
13923        async move {
13924            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13925            assert_eq!(
13926                params.text_document_position.position,
13927                complete_from_position
13928            );
13929            Ok(Some(lsp::CompletionResponse::Array(
13930                completions
13931                    .iter()
13932                    .map(|completion_text| lsp::CompletionItem {
13933                        label: completion_text.to_string(),
13934                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13935                            range: replace_range,
13936                            new_text: completion_text.to_string(),
13937                        })),
13938                        ..Default::default()
13939                    })
13940                    .collect(),
13941            )))
13942        }
13943    });
13944
13945    async move {
13946        request.next().await;
13947    }
13948}
13949
13950fn handle_resolve_completion_request(
13951    cx: &mut EditorLspTestContext,
13952    edits: Option<Vec<(&'static str, &'static str)>>,
13953) -> impl Future<Output = ()> {
13954    let edits = edits.map(|edits| {
13955        edits
13956            .iter()
13957            .map(|(marked_string, new_text)| {
13958                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13959                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13960                lsp::TextEdit::new(replace_range, new_text.to_string())
13961            })
13962            .collect::<Vec<_>>()
13963    });
13964
13965    let mut request =
13966        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13967            let edits = edits.clone();
13968            async move {
13969                Ok(lsp::CompletionItem {
13970                    additional_text_edits: edits,
13971                    ..Default::default()
13972                })
13973            }
13974        });
13975
13976    async move {
13977        request.next().await;
13978    }
13979}
13980
13981pub(crate) fn update_test_language_settings(
13982    cx: &mut TestAppContext,
13983    f: impl Fn(&mut AllLanguageSettingsContent),
13984) {
13985    cx.update(|cx| {
13986        SettingsStore::update_global(cx, |store, cx| {
13987            store.update_user_settings::<AllLanguageSettings>(cx, f);
13988        });
13989    });
13990}
13991
13992pub(crate) fn update_test_project_settings(
13993    cx: &mut TestAppContext,
13994    f: impl Fn(&mut ProjectSettings),
13995) {
13996    cx.update(|cx| {
13997        SettingsStore::update_global(cx, |store, cx| {
13998            store.update_user_settings::<ProjectSettings>(cx, f);
13999        });
14000    });
14001}
14002
14003pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14004    cx.update(|cx| {
14005        assets::Assets.load_test_fonts(cx);
14006        let store = SettingsStore::test(cx);
14007        cx.set_global(store);
14008        theme::init(theme::LoadThemes::JustBase, cx);
14009        release_channel::init(SemanticVersion::default(), cx);
14010        client::init_settings(cx);
14011        language::init(cx);
14012        Project::init_settings(cx);
14013        workspace::init_settings(cx);
14014        crate::init(cx);
14015    });
14016
14017    update_test_language_settings(cx, f);
14018}
14019
14020#[track_caller]
14021fn assert_hunk_revert(
14022    not_reverted_text_with_selections: &str,
14023    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14024    expected_reverted_text_with_selections: &str,
14025    base_text: &str,
14026    cx: &mut EditorLspTestContext,
14027) {
14028    cx.set_state(not_reverted_text_with_selections);
14029    cx.update_editor(|editor, cx| {
14030        editor
14031            .buffer()
14032            .read(cx)
14033            .as_singleton()
14034            .unwrap()
14035            .update(cx, |buffer, cx| {
14036                buffer.set_diff_base(Some(base_text.into()), cx);
14037            });
14038    });
14039    cx.executor().run_until_parked();
14040
14041    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14042        let snapshot = editor.buffer().read(cx).snapshot(cx);
14043        let reverted_hunk_statuses = snapshot
14044            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
14045            .map(|hunk| hunk_status(&hunk))
14046            .collect::<Vec<_>>();
14047
14048        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14049        reverted_hunk_statuses
14050    });
14051    cx.executor().run_until_parked();
14052    cx.assert_editor_state(expected_reverted_text_with_selections);
14053    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14054}