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_resolve_updates_labels(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 completion_item = lsp::CompletionItem {
10649        label: "unresolved".to_string(),
10650        detail: None,
10651        documentation: None,
10652        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10653            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10654            new_text: ".unresolved".to_string(),
10655        })),
10656        ..lsp::CompletionItem::default()
10657    };
10658
10659    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10660        let item = completion_item.clone();
10661        async move { Ok(Some(lsp::CompletionResponse::Array(vec![item]))) }
10662    })
10663    .next()
10664    .await;
10665
10666    cx.condition(|editor, _| editor.context_menu_visible())
10667        .await;
10668    cx.update_editor(|editor, _| {
10669        let context_menu = editor.context_menu.read();
10670        let context_menu = context_menu
10671            .as_ref()
10672            .expect("Should have the context menu deployed");
10673        match context_menu {
10674            ContextMenu::Completions(completions_menu) => {
10675                let completions = completions_menu.completions.read();
10676                assert_eq!(completions.len(), 1, "Should have one completion");
10677                assert_eq!(completions.get(0).unwrap().label.text, "unresolved");
10678            }
10679            ContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10680        }
10681    });
10682
10683    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10684        Ok(lsp::CompletionItem {
10685            label: "resolved".to_string(),
10686            detail: Some("Now resolved!".to_string()),
10687            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10688            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10689                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10690                new_text: ".resolved".to_string(),
10691            })),
10692            ..lsp::CompletionItem::default()
10693        })
10694    })
10695    .next()
10696    .await;
10697    cx.run_until_parked();
10698
10699    cx.update_editor(|editor, _| {
10700        let context_menu = editor.context_menu.read();
10701        let context_menu = context_menu
10702            .as_ref()
10703            .expect("Should have the context menu deployed");
10704        match context_menu {
10705            ContextMenu::Completions(completions_menu) => {
10706                let completions = completions_menu.completions.read();
10707                assert_eq!(completions.len(), 1, "Should have one completion");
10708                assert_eq!(
10709                    completions.get(0).unwrap().label.text,
10710                    "resolved",
10711                    "Should update the completion label after resolving"
10712                );
10713            }
10714            ContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10715        }
10716    });
10717}
10718
10719#[gpui::test]
10720async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10721    init_test(cx, |_| {});
10722
10723    let mut cx = EditorLspTestContext::new_rust(
10724        lsp::ServerCapabilities {
10725            completion_provider: Some(lsp::CompletionOptions {
10726                trigger_characters: Some(vec![".".to_string()]),
10727                resolve_provider: Some(true),
10728                ..Default::default()
10729            }),
10730            ..Default::default()
10731        },
10732        cx,
10733    )
10734    .await;
10735
10736    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10737    cx.simulate_keystroke(".");
10738
10739    let default_commit_characters = vec!["?".to_string()];
10740    let default_data = json!({ "very": "special"});
10741    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10742    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10743    let default_edit_range = lsp::Range {
10744        start: lsp::Position {
10745            line: 0,
10746            character: 5,
10747        },
10748        end: lsp::Position {
10749            line: 0,
10750            character: 5,
10751        },
10752    };
10753
10754    let resolve_requests_number = Arc::new(AtomicUsize::new(0));
10755    let expect_first_item = Arc::new(AtomicBool::new(true));
10756    cx.lsp
10757        .server
10758        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10759            let closure_default_data = default_data.clone();
10760            let closure_resolve_requests_number = resolve_requests_number.clone();
10761            let closure_expect_first_item = expect_first_item.clone();
10762            let closure_default_commit_characters = default_commit_characters.clone();
10763            move |item_to_resolve, _| {
10764                closure_resolve_requests_number.fetch_add(1, atomic::Ordering::Release);
10765                let default_data = closure_default_data.clone();
10766                let default_commit_characters = closure_default_commit_characters.clone();
10767                let expect_first_item = closure_expect_first_item.clone();
10768                async move {
10769                    if expect_first_item.load(atomic::Ordering::Acquire) {
10770                        assert_eq!(
10771                            item_to_resolve.label, "Some(2)",
10772                            "Should have selected the first item"
10773                        );
10774                        assert_eq!(
10775                            item_to_resolve.data,
10776                            Some(json!({ "very": "special"})),
10777                            "First item should bring its own data for resolving"
10778                        );
10779                        assert_eq!(
10780                            item_to_resolve.commit_characters,
10781                            Some(default_commit_characters),
10782                            "First item had no own commit characters and should inherit the default ones"
10783                        );
10784                        assert!(
10785                            matches!(
10786                                item_to_resolve.text_edit,
10787                                Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10788                            ),
10789                            "First item should bring its own edit range for resolving"
10790                        );
10791                        assert_eq!(
10792                            item_to_resolve.insert_text_format,
10793                            Some(default_insert_text_format),
10794                            "First item had no own insert text format and should inherit the default one"
10795                        );
10796                        assert_eq!(
10797                            item_to_resolve.insert_text_mode,
10798                            Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10799                            "First item should bring its own insert text mode for resolving"
10800                        );
10801                        Ok(item_to_resolve)
10802                    } else {
10803                        assert_eq!(
10804                            item_to_resolve.label, "vec![2]",
10805                            "Should have selected the last item"
10806                        );
10807                        assert_eq!(
10808                            item_to_resolve.data,
10809                            Some(default_data),
10810                            "Last item has no own resolve data and should inherit the default one"
10811                        );
10812                        assert_eq!(
10813                            item_to_resolve.commit_characters,
10814                            Some(default_commit_characters),
10815                            "Last item had no own commit characters and should inherit the default ones"
10816                        );
10817                        assert_eq!(
10818                            item_to_resolve.text_edit,
10819                            Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10820                                range: default_edit_range,
10821                                new_text: "vec![2]".to_string()
10822                            })),
10823                            "Last item had no own edit range and should inherit the default one"
10824                        );
10825                        assert_eq!(
10826                            item_to_resolve.insert_text_format,
10827                            Some(lsp::InsertTextFormat::PLAIN_TEXT),
10828                            "Last item should bring its own insert text format for resolving"
10829                        );
10830                        assert_eq!(
10831                            item_to_resolve.insert_text_mode,
10832                            Some(default_insert_text_mode),
10833                            "Last item had no own insert text mode and should inherit the default one"
10834                        );
10835
10836                        Ok(item_to_resolve)
10837                    }
10838                }
10839            }
10840        }).detach();
10841
10842    let completion_data = default_data.clone();
10843    let completion_characters = default_commit_characters.clone();
10844    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10845        let default_data = completion_data.clone();
10846        let default_commit_characters = completion_characters.clone();
10847        async move {
10848            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10849                items: vec![
10850                    lsp::CompletionItem {
10851                        label: "Some(2)".into(),
10852                        insert_text: Some("Some(2)".into()),
10853                        data: Some(json!({ "very": "special"})),
10854                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10855                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10856                            lsp::InsertReplaceEdit {
10857                                new_text: "Some(2)".to_string(),
10858                                insert: lsp::Range::default(),
10859                                replace: lsp::Range::default(),
10860                            },
10861                        )),
10862                        ..lsp::CompletionItem::default()
10863                    },
10864                    lsp::CompletionItem {
10865                        label: "vec![2]".into(),
10866                        insert_text: Some("vec![2]".into()),
10867                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10868                        ..lsp::CompletionItem::default()
10869                    },
10870                ],
10871                item_defaults: Some(lsp::CompletionListItemDefaults {
10872                    data: Some(default_data.clone()),
10873                    commit_characters: Some(default_commit_characters.clone()),
10874                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10875                        default_edit_range,
10876                    )),
10877                    insert_text_format: Some(default_insert_text_format),
10878                    insert_text_mode: Some(default_insert_text_mode),
10879                }),
10880                ..lsp::CompletionList::default()
10881            })))
10882        }
10883    })
10884    .next()
10885    .await;
10886
10887    cx.condition(|editor, _| editor.context_menu_visible())
10888        .await;
10889    cx.run_until_parked();
10890    cx.update_editor(|editor, _| {
10891        let menu = editor.context_menu.read();
10892        match menu.as_ref().expect("should have the completions menu") {
10893            ContextMenu::Completions(completions_menu) => {
10894                assert_eq!(
10895                    completions_menu
10896                        .matches
10897                        .iter()
10898                        .map(|c| c.string.as_str())
10899                        .collect::<Vec<_>>(),
10900                    vec!["Some(2)", "vec![2]"]
10901                );
10902            }
10903            ContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10904        }
10905    });
10906    assert_eq!(
10907        resolve_requests_number.load(atomic::Ordering::Acquire),
10908        1,
10909        "While there are 2 items in the completion list, only 1 resolve request should have been sent, for the selected item"
10910    );
10911
10912    cx.update_editor(|editor, cx| {
10913        editor.context_menu_first(&ContextMenuFirst, cx);
10914    });
10915    cx.run_until_parked();
10916    assert_eq!(
10917        resolve_requests_number.load(atomic::Ordering::Acquire),
10918        2,
10919        "After re-selecting the first item, another resolve request should have been sent"
10920    );
10921
10922    expect_first_item.store(false, atomic::Ordering::Release);
10923    cx.update_editor(|editor, cx| {
10924        editor.context_menu_last(&ContextMenuLast, cx);
10925    });
10926    cx.run_until_parked();
10927    assert_eq!(
10928        resolve_requests_number.load(atomic::Ordering::Acquire),
10929        3,
10930        "After selecting the other item, another resolve request should have been sent"
10931    );
10932}
10933
10934#[gpui::test]
10935async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10936    init_test(cx, |_| {});
10937
10938    let mut cx = EditorLspTestContext::new(
10939        Language::new(
10940            LanguageConfig {
10941                matcher: LanguageMatcher {
10942                    path_suffixes: vec!["jsx".into()],
10943                    ..Default::default()
10944                },
10945                overrides: [(
10946                    "element".into(),
10947                    LanguageConfigOverride {
10948                        word_characters: Override::Set(['-'].into_iter().collect()),
10949                        ..Default::default()
10950                    },
10951                )]
10952                .into_iter()
10953                .collect(),
10954                ..Default::default()
10955            },
10956            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10957        )
10958        .with_override_query("(jsx_self_closing_element) @element")
10959        .unwrap(),
10960        lsp::ServerCapabilities {
10961            completion_provider: Some(lsp::CompletionOptions {
10962                trigger_characters: Some(vec![":".to_string()]),
10963                ..Default::default()
10964            }),
10965            ..Default::default()
10966        },
10967        cx,
10968    )
10969    .await;
10970
10971    cx.lsp
10972        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10973            Ok(Some(lsp::CompletionResponse::Array(vec![
10974                lsp::CompletionItem {
10975                    label: "bg-blue".into(),
10976                    ..Default::default()
10977                },
10978                lsp::CompletionItem {
10979                    label: "bg-red".into(),
10980                    ..Default::default()
10981                },
10982                lsp::CompletionItem {
10983                    label: "bg-yellow".into(),
10984                    ..Default::default()
10985                },
10986            ])))
10987        });
10988
10989    cx.set_state(r#"<p class="bgˇ" />"#);
10990
10991    // Trigger completion when typing a dash, because the dash is an extra
10992    // word character in the 'element' scope, which contains the cursor.
10993    cx.simulate_keystroke("-");
10994    cx.executor().run_until_parked();
10995    cx.update_editor(|editor, _| {
10996        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10997            assert_eq!(
10998                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10999                &["bg-red", "bg-blue", "bg-yellow"]
11000            );
11001        } else {
11002            panic!("expected completion menu to be open");
11003        }
11004    });
11005
11006    cx.simulate_keystroke("l");
11007    cx.executor().run_until_parked();
11008    cx.update_editor(|editor, _| {
11009        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
11010            assert_eq!(
11011                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
11012                &["bg-blue", "bg-yellow"]
11013            );
11014        } else {
11015            panic!("expected completion menu to be open");
11016        }
11017    });
11018
11019    // When filtering completions, consider the character after the '-' to
11020    // be the start of a subword.
11021    cx.set_state(r#"<p class="yelˇ" />"#);
11022    cx.simulate_keystroke("l");
11023    cx.executor().run_until_parked();
11024    cx.update_editor(|editor, _| {
11025        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
11026            assert_eq!(
11027                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
11028                &["bg-yellow"]
11029            );
11030        } else {
11031            panic!("expected completion menu to be open");
11032        }
11033    });
11034}
11035
11036#[gpui::test]
11037async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11038    init_test(cx, |settings| {
11039        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11040            FormatterList(vec![Formatter::Prettier].into()),
11041        ))
11042    });
11043
11044    let fs = FakeFs::new(cx.executor());
11045    fs.insert_file("/file.ts", Default::default()).await;
11046
11047    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11048    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11049
11050    language_registry.add(Arc::new(Language::new(
11051        LanguageConfig {
11052            name: "TypeScript".into(),
11053            matcher: LanguageMatcher {
11054                path_suffixes: vec!["ts".to_string()],
11055                ..Default::default()
11056            },
11057            ..Default::default()
11058        },
11059        Some(tree_sitter_rust::LANGUAGE.into()),
11060    )));
11061    update_test_language_settings(cx, |settings| {
11062        settings.defaults.prettier = Some(PrettierSettings {
11063            allowed: true,
11064            ..PrettierSettings::default()
11065        });
11066    });
11067
11068    let test_plugin = "test_plugin";
11069    let _ = language_registry.register_fake_lsp(
11070        "TypeScript",
11071        FakeLspAdapter {
11072            prettier_plugins: vec![test_plugin],
11073            ..Default::default()
11074        },
11075    );
11076
11077    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11078    let buffer = project
11079        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11080        .await
11081        .unwrap();
11082
11083    let buffer_text = "one\ntwo\nthree\n";
11084    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11085    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11086    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11087
11088    editor
11089        .update(cx, |editor, cx| {
11090            editor.perform_format(
11091                project.clone(),
11092                FormatTrigger::Manual,
11093                FormatTarget::Buffer,
11094                cx,
11095            )
11096        })
11097        .unwrap()
11098        .await;
11099    assert_eq!(
11100        editor.update(cx, |editor, cx| editor.text(cx)),
11101        buffer_text.to_string() + prettier_format_suffix,
11102        "Test prettier formatting was not applied to the original buffer text",
11103    );
11104
11105    update_test_language_settings(cx, |settings| {
11106        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11107    });
11108    let format = editor.update(cx, |editor, cx| {
11109        editor.perform_format(
11110            project.clone(),
11111            FormatTrigger::Manual,
11112            FormatTarget::Buffer,
11113            cx,
11114        )
11115    });
11116    format.await.unwrap();
11117    assert_eq!(
11118        editor.update(cx, |editor, cx| editor.text(cx)),
11119        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11120        "Autoformatting (via test prettier) was not applied to the original buffer text",
11121    );
11122}
11123
11124#[gpui::test]
11125async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11126    init_test(cx, |_| {});
11127    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11128    let base_text = indoc! {r#"struct Row;
11129struct Row1;
11130struct Row2;
11131
11132struct Row4;
11133struct Row5;
11134struct Row6;
11135
11136struct Row8;
11137struct Row9;
11138struct Row10;"#};
11139
11140    // When addition hunks are not adjacent to carets, no hunk revert is performed
11141    assert_hunk_revert(
11142        indoc! {r#"struct Row;
11143                   struct Row1;
11144                   struct Row1.1;
11145                   struct Row1.2;
11146                   struct Row2;ˇ
11147
11148                   struct Row4;
11149                   struct Row5;
11150                   struct Row6;
11151
11152                   struct Row8;
11153                   ˇstruct Row9;
11154                   struct Row9.1;
11155                   struct Row9.2;
11156                   struct Row9.3;
11157                   struct Row10;"#},
11158        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11159        indoc! {r#"struct Row;
11160                   struct Row1;
11161                   struct Row1.1;
11162                   struct Row1.2;
11163                   struct Row2;ˇ
11164
11165                   struct Row4;
11166                   struct Row5;
11167                   struct Row6;
11168
11169                   struct Row8;
11170                   ˇstruct Row9;
11171                   struct Row9.1;
11172                   struct Row9.2;
11173                   struct Row9.3;
11174                   struct Row10;"#},
11175        base_text,
11176        &mut cx,
11177    );
11178    // Same for selections
11179    assert_hunk_revert(
11180        indoc! {r#"struct Row;
11181                   struct Row1;
11182                   struct Row2;
11183                   struct Row2.1;
11184                   struct Row2.2;
11185                   «ˇ
11186                   struct Row4;
11187                   struct» Row5;
11188                   «struct Row6;
11189                   ˇ»
11190                   struct Row9.1;
11191                   struct Row9.2;
11192                   struct Row9.3;
11193                   struct Row8;
11194                   struct Row9;
11195                   struct Row10;"#},
11196        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11197        indoc! {r#"struct Row;
11198                   struct Row1;
11199                   struct Row2;
11200                   struct Row2.1;
11201                   struct Row2.2;
11202                   «ˇ
11203                   struct Row4;
11204                   struct» Row5;
11205                   «struct Row6;
11206                   ˇ»
11207                   struct Row9.1;
11208                   struct Row9.2;
11209                   struct Row9.3;
11210                   struct Row8;
11211                   struct Row9;
11212                   struct Row10;"#},
11213        base_text,
11214        &mut cx,
11215    );
11216
11217    // When carets and selections intersect the addition hunks, those are reverted.
11218    // Adjacent carets got merged.
11219    assert_hunk_revert(
11220        indoc! {r#"struct Row;
11221                   ˇ// something on the top
11222                   struct Row1;
11223                   struct Row2;
11224                   struct Roˇw3.1;
11225                   struct Row2.2;
11226                   struct Row2.3;ˇ
11227
11228                   struct Row4;
11229                   struct ˇRow5.1;
11230                   struct Row5.2;
11231                   struct «Rowˇ»5.3;
11232                   struct Row5;
11233                   struct Row6;
11234                   ˇ
11235                   struct Row9.1;
11236                   struct «Rowˇ»9.2;
11237                   struct «ˇRow»9.3;
11238                   struct Row8;
11239                   struct Row9;
11240                   «ˇ// something on bottom»
11241                   struct Row10;"#},
11242        vec![
11243            DiffHunkStatus::Added,
11244            DiffHunkStatus::Added,
11245            DiffHunkStatus::Added,
11246            DiffHunkStatus::Added,
11247            DiffHunkStatus::Added,
11248        ],
11249        indoc! {r#"struct Row;
11250                   ˇstruct Row1;
11251                   struct Row2;
11252                   ˇ
11253                   struct Row4;
11254                   ˇstruct Row5;
11255                   struct Row6;
11256                   ˇ
11257                   ˇstruct Row8;
11258                   struct Row9;
11259                   ˇstruct Row10;"#},
11260        base_text,
11261        &mut cx,
11262    );
11263}
11264
11265#[gpui::test]
11266async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11267    init_test(cx, |_| {});
11268    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11269    let base_text = indoc! {r#"struct Row;
11270struct Row1;
11271struct Row2;
11272
11273struct Row4;
11274struct Row5;
11275struct Row6;
11276
11277struct Row8;
11278struct Row9;
11279struct Row10;"#};
11280
11281    // Modification hunks behave the same as the addition ones.
11282    assert_hunk_revert(
11283        indoc! {r#"struct Row;
11284                   struct Row1;
11285                   struct Row33;
11286                   ˇ
11287                   struct Row4;
11288                   struct Row5;
11289                   struct Row6;
11290                   ˇ
11291                   struct Row99;
11292                   struct Row9;
11293                   struct Row10;"#},
11294        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11295        indoc! {r#"struct Row;
11296                   struct Row1;
11297                   struct Row33;
11298                   ˇ
11299                   struct Row4;
11300                   struct Row5;
11301                   struct Row6;
11302                   ˇ
11303                   struct Row99;
11304                   struct Row9;
11305                   struct Row10;"#},
11306        base_text,
11307        &mut cx,
11308    );
11309    assert_hunk_revert(
11310        indoc! {r#"struct Row;
11311                   struct Row1;
11312                   struct Row33;
11313                   «ˇ
11314                   struct Row4;
11315                   struct» Row5;
11316                   «struct Row6;
11317                   ˇ»
11318                   struct Row99;
11319                   struct Row9;
11320                   struct Row10;"#},
11321        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11322        indoc! {r#"struct Row;
11323                   struct Row1;
11324                   struct Row33;
11325                   «ˇ
11326                   struct Row4;
11327                   struct» Row5;
11328                   «struct Row6;
11329                   ˇ»
11330                   struct Row99;
11331                   struct Row9;
11332                   struct Row10;"#},
11333        base_text,
11334        &mut cx,
11335    );
11336
11337    assert_hunk_revert(
11338        indoc! {r#"ˇstruct Row1.1;
11339                   struct Row1;
11340                   «ˇstr»uct Row22;
11341
11342                   struct ˇRow44;
11343                   struct Row5;
11344                   struct «Rˇ»ow66;ˇ
11345
11346                   «struˇ»ct Row88;
11347                   struct Row9;
11348                   struct Row1011;ˇ"#},
11349        vec![
11350            DiffHunkStatus::Modified,
11351            DiffHunkStatus::Modified,
11352            DiffHunkStatus::Modified,
11353            DiffHunkStatus::Modified,
11354            DiffHunkStatus::Modified,
11355            DiffHunkStatus::Modified,
11356        ],
11357        indoc! {r#"struct Row;
11358                   ˇstruct Row1;
11359                   struct Row2;
11360                   ˇ
11361                   struct Row4;
11362                   ˇstruct Row5;
11363                   struct Row6;
11364                   ˇ
11365                   struct Row8;
11366                   ˇstruct Row9;
11367                   struct Row10;ˇ"#},
11368        base_text,
11369        &mut cx,
11370    );
11371}
11372
11373#[gpui::test]
11374async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11375    init_test(cx, |_| {});
11376    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11377    let base_text = indoc! {r#"struct Row;
11378struct Row1;
11379struct Row2;
11380
11381struct Row4;
11382struct Row5;
11383struct Row6;
11384
11385struct Row8;
11386struct Row9;
11387struct Row10;"#};
11388
11389    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11390    assert_hunk_revert(
11391        indoc! {r#"struct Row;
11392                   struct Row2;
11393
11394                   ˇstruct Row4;
11395                   struct Row5;
11396                   struct Row6;
11397                   ˇ
11398                   struct Row8;
11399                   struct Row10;"#},
11400        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11401        indoc! {r#"struct Row;
11402                   struct Row2;
11403
11404                   ˇstruct Row4;
11405                   struct Row5;
11406                   struct Row6;
11407                   ˇ
11408                   struct Row8;
11409                   struct Row10;"#},
11410        base_text,
11411        &mut cx,
11412    );
11413    assert_hunk_revert(
11414        indoc! {r#"struct Row;
11415                   struct Row2;
11416
11417                   «ˇstruct Row4;
11418                   struct» Row5;
11419                   «struct Row6;
11420                   ˇ»
11421                   struct Row8;
11422                   struct Row10;"#},
11423        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11424        indoc! {r#"struct Row;
11425                   struct Row2;
11426
11427                   «ˇstruct Row4;
11428                   struct» Row5;
11429                   «struct Row6;
11430                   ˇ»
11431                   struct Row8;
11432                   struct Row10;"#},
11433        base_text,
11434        &mut cx,
11435    );
11436
11437    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11438    assert_hunk_revert(
11439        indoc! {r#"struct Row;
11440                   ˇstruct Row2;
11441
11442                   struct Row4;
11443                   struct Row5;
11444                   struct Row6;
11445
11446                   struct Row8;ˇ
11447                   struct Row10;"#},
11448        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11449        indoc! {r#"struct Row;
11450                   struct Row1;
11451                   ˇstruct Row2;
11452
11453                   struct Row4;
11454                   struct Row5;
11455                   struct Row6;
11456
11457                   struct Row8;ˇ
11458                   struct Row9;
11459                   struct Row10;"#},
11460        base_text,
11461        &mut cx,
11462    );
11463    assert_hunk_revert(
11464        indoc! {r#"struct Row;
11465                   struct Row2«ˇ;
11466                   struct Row4;
11467                   struct» Row5;
11468                   «struct Row6;
11469
11470                   struct Row8;ˇ»
11471                   struct Row10;"#},
11472        vec![
11473            DiffHunkStatus::Removed,
11474            DiffHunkStatus::Removed,
11475            DiffHunkStatus::Removed,
11476        ],
11477        indoc! {r#"struct Row;
11478                   struct Row1;
11479                   struct Row2«ˇ;
11480
11481                   struct Row4;
11482                   struct» Row5;
11483                   «struct Row6;
11484
11485                   struct Row8;ˇ»
11486                   struct Row9;
11487                   struct Row10;"#},
11488        base_text,
11489        &mut cx,
11490    );
11491}
11492
11493#[gpui::test]
11494async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11495    init_test(cx, |_| {});
11496
11497    let cols = 4;
11498    let rows = 10;
11499    let sample_text_1 = sample_text(rows, cols, 'a');
11500    assert_eq!(
11501        sample_text_1,
11502        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11503    );
11504    let sample_text_2 = sample_text(rows, cols, 'l');
11505    assert_eq!(
11506        sample_text_2,
11507        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11508    );
11509    let sample_text_3 = sample_text(rows, cols, 'v');
11510    assert_eq!(
11511        sample_text_3,
11512        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11513    );
11514
11515    fn diff_every_buffer_row(
11516        buffer: &Model<Buffer>,
11517        sample_text: String,
11518        cols: usize,
11519        cx: &mut gpui::TestAppContext,
11520    ) {
11521        // revert first character in each row, creating one large diff hunk per buffer
11522        let is_first_char = |offset: usize| offset % cols == 0;
11523        buffer.update(cx, |buffer, cx| {
11524            buffer.set_text(
11525                sample_text
11526                    .chars()
11527                    .enumerate()
11528                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11529                    .collect::<String>(),
11530                cx,
11531            );
11532            buffer.set_diff_base(Some(sample_text), cx);
11533        });
11534        cx.executor().run_until_parked();
11535    }
11536
11537    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11538    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11539
11540    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11541    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11542
11543    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11544    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11545
11546    let multibuffer = cx.new_model(|cx| {
11547        let mut multibuffer = MultiBuffer::new(ReadWrite);
11548        multibuffer.push_excerpts(
11549            buffer_1.clone(),
11550            [
11551                ExcerptRange {
11552                    context: Point::new(0, 0)..Point::new(3, 0),
11553                    primary: None,
11554                },
11555                ExcerptRange {
11556                    context: Point::new(5, 0)..Point::new(7, 0),
11557                    primary: None,
11558                },
11559                ExcerptRange {
11560                    context: Point::new(9, 0)..Point::new(10, 4),
11561                    primary: None,
11562                },
11563            ],
11564            cx,
11565        );
11566        multibuffer.push_excerpts(
11567            buffer_2.clone(),
11568            [
11569                ExcerptRange {
11570                    context: Point::new(0, 0)..Point::new(3, 0),
11571                    primary: None,
11572                },
11573                ExcerptRange {
11574                    context: Point::new(5, 0)..Point::new(7, 0),
11575                    primary: None,
11576                },
11577                ExcerptRange {
11578                    context: Point::new(9, 0)..Point::new(10, 4),
11579                    primary: None,
11580                },
11581            ],
11582            cx,
11583        );
11584        multibuffer.push_excerpts(
11585            buffer_3.clone(),
11586            [
11587                ExcerptRange {
11588                    context: Point::new(0, 0)..Point::new(3, 0),
11589                    primary: None,
11590                },
11591                ExcerptRange {
11592                    context: Point::new(5, 0)..Point::new(7, 0),
11593                    primary: None,
11594                },
11595                ExcerptRange {
11596                    context: Point::new(9, 0)..Point::new(10, 4),
11597                    primary: None,
11598                },
11599            ],
11600            cx,
11601        );
11602        multibuffer
11603    });
11604
11605    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11606    editor.update(cx, |editor, cx| {
11607        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");
11608        editor.select_all(&SelectAll, cx);
11609        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11610    });
11611    cx.executor().run_until_parked();
11612    // When all ranges are selected, all buffer hunks are reverted.
11613    editor.update(cx, |editor, cx| {
11614        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");
11615    });
11616    buffer_1.update(cx, |buffer, _| {
11617        assert_eq!(buffer.text(), sample_text_1);
11618    });
11619    buffer_2.update(cx, |buffer, _| {
11620        assert_eq!(buffer.text(), sample_text_2);
11621    });
11622    buffer_3.update(cx, |buffer, _| {
11623        assert_eq!(buffer.text(), sample_text_3);
11624    });
11625
11626    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11627    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11628    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11629    editor.update(cx, |editor, cx| {
11630        editor.change_selections(None, cx, |s| {
11631            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11632        });
11633        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11634    });
11635    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11636    // but not affect buffer_2 and its related excerpts.
11637    editor.update(cx, |editor, cx| {
11638        assert_eq!(
11639            editor.text(cx),
11640            "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"
11641        );
11642    });
11643    buffer_1.update(cx, |buffer, _| {
11644        assert_eq!(buffer.text(), sample_text_1);
11645    });
11646    buffer_2.update(cx, |buffer, _| {
11647        assert_eq!(
11648            buffer.text(),
11649            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11650        );
11651    });
11652    buffer_3.update(cx, |buffer, _| {
11653        assert_eq!(
11654            buffer.text(),
11655            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11656        );
11657    });
11658}
11659
11660#[gpui::test]
11661async fn test_multibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11662    init_test(cx, |_| {});
11663
11664    let cols = 4;
11665    let rows = 10;
11666    let sample_text_1 = sample_text(rows, cols, 'a');
11667    assert_eq!(
11668        sample_text_1,
11669        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11670    );
11671    let sample_text_2 = sample_text(rows, cols, 'l');
11672    assert_eq!(
11673        sample_text_2,
11674        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11675    );
11676    let sample_text_3 = sample_text(rows, cols, 'v');
11677    assert_eq!(
11678        sample_text_3,
11679        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11680    );
11681
11682    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11683    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11684    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11685
11686    let multi_buffer = cx.new_model(|cx| {
11687        let mut multibuffer = MultiBuffer::new(ReadWrite);
11688        multibuffer.push_excerpts(
11689            buffer_1.clone(),
11690            [
11691                ExcerptRange {
11692                    context: Point::new(0, 0)..Point::new(3, 0),
11693                    primary: None,
11694                },
11695                ExcerptRange {
11696                    context: Point::new(5, 0)..Point::new(7, 0),
11697                    primary: None,
11698                },
11699                ExcerptRange {
11700                    context: Point::new(9, 0)..Point::new(10, 4),
11701                    primary: None,
11702                },
11703            ],
11704            cx,
11705        );
11706        multibuffer.push_excerpts(
11707            buffer_2.clone(),
11708            [
11709                ExcerptRange {
11710                    context: Point::new(0, 0)..Point::new(3, 0),
11711                    primary: None,
11712                },
11713                ExcerptRange {
11714                    context: Point::new(5, 0)..Point::new(7, 0),
11715                    primary: None,
11716                },
11717                ExcerptRange {
11718                    context: Point::new(9, 0)..Point::new(10, 4),
11719                    primary: None,
11720                },
11721            ],
11722            cx,
11723        );
11724        multibuffer.push_excerpts(
11725            buffer_3.clone(),
11726            [
11727                ExcerptRange {
11728                    context: Point::new(0, 0)..Point::new(3, 0),
11729                    primary: None,
11730                },
11731                ExcerptRange {
11732                    context: Point::new(5, 0)..Point::new(7, 0),
11733                    primary: None,
11734                },
11735                ExcerptRange {
11736                    context: Point::new(9, 0)..Point::new(10, 4),
11737                    primary: None,
11738                },
11739            ],
11740            cx,
11741        );
11742        multibuffer
11743    });
11744
11745    let fs = FakeFs::new(cx.executor());
11746    fs.insert_tree(
11747        "/a",
11748        json!({
11749            "main.rs": sample_text_1,
11750            "other.rs": sample_text_2,
11751            "lib.rs": sample_text_3,
11752        }),
11753    )
11754    .await;
11755    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11756    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11757    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11758    let multi_buffer_editor = cx.new_view(|cx| {
11759        Editor::new(
11760            EditorMode::Full,
11761            multi_buffer,
11762            Some(project.clone()),
11763            true,
11764            cx,
11765        )
11766    });
11767    let multibuffer_item_id = workspace
11768        .update(cx, |workspace, cx| {
11769            assert!(
11770                workspace.active_item(cx).is_none(),
11771                "active item should be None before the first item is added"
11772            );
11773            workspace.add_item_to_active_pane(
11774                Box::new(multi_buffer_editor.clone()),
11775                None,
11776                true,
11777                cx,
11778            );
11779            let active_item = workspace
11780                .active_item(cx)
11781                .expect("should have an active item after adding the multi buffer");
11782            assert!(
11783                !active_item.is_singleton(cx),
11784                "A multi buffer was expected to active after adding"
11785            );
11786            active_item.item_id()
11787        })
11788        .unwrap();
11789    cx.executor().run_until_parked();
11790
11791    multi_buffer_editor.update(cx, |editor, cx| {
11792        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11793        editor.open_excerpts(&OpenExcerpts, cx);
11794    });
11795    cx.executor().run_until_parked();
11796    let first_item_id = workspace
11797        .update(cx, |workspace, cx| {
11798            let active_item = workspace
11799                .active_item(cx)
11800                .expect("should have an active item after navigating into the 1st buffer");
11801            let first_item_id = active_item.item_id();
11802            assert_ne!(
11803                first_item_id, multibuffer_item_id,
11804                "Should navigate into the 1st buffer and activate it"
11805            );
11806            assert!(
11807                active_item.is_singleton(cx),
11808                "New active item should be a singleton buffer"
11809            );
11810            assert_eq!(
11811                active_item
11812                    .act_as::<Editor>(cx)
11813                    .expect("should have navigated into an editor for the 1st buffer")
11814                    .read(cx)
11815                    .text(cx),
11816                sample_text_1
11817            );
11818
11819            workspace
11820                .go_back(workspace.active_pane().downgrade(), cx)
11821                .detach_and_log_err(cx);
11822
11823            first_item_id
11824        })
11825        .unwrap();
11826    cx.executor().run_until_parked();
11827    workspace
11828        .update(cx, |workspace, cx| {
11829            let active_item = workspace
11830                .active_item(cx)
11831                .expect("should have an active item after navigating back");
11832            assert_eq!(
11833                active_item.item_id(),
11834                multibuffer_item_id,
11835                "Should navigate back to the multi buffer"
11836            );
11837            assert!(!active_item.is_singleton(cx));
11838        })
11839        .unwrap();
11840
11841    multi_buffer_editor.update(cx, |editor, cx| {
11842        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11843            s.select_ranges(Some(39..40))
11844        });
11845        editor.open_excerpts(&OpenExcerpts, cx);
11846    });
11847    cx.executor().run_until_parked();
11848    let second_item_id = workspace
11849        .update(cx, |workspace, cx| {
11850            let active_item = workspace
11851                .active_item(cx)
11852                .expect("should have an active item after navigating into the 2nd buffer");
11853            let second_item_id = active_item.item_id();
11854            assert_ne!(
11855                second_item_id, multibuffer_item_id,
11856                "Should navigate away from the multibuffer"
11857            );
11858            assert_ne!(
11859                second_item_id, first_item_id,
11860                "Should navigate into the 2nd buffer and activate it"
11861            );
11862            assert!(
11863                active_item.is_singleton(cx),
11864                "New active item should be a singleton buffer"
11865            );
11866            assert_eq!(
11867                active_item
11868                    .act_as::<Editor>(cx)
11869                    .expect("should have navigated into an editor")
11870                    .read(cx)
11871                    .text(cx),
11872                sample_text_2
11873            );
11874
11875            workspace
11876                .go_back(workspace.active_pane().downgrade(), cx)
11877                .detach_and_log_err(cx);
11878
11879            second_item_id
11880        })
11881        .unwrap();
11882    cx.executor().run_until_parked();
11883    workspace
11884        .update(cx, |workspace, cx| {
11885            let active_item = workspace
11886                .active_item(cx)
11887                .expect("should have an active item after navigating back from the 2nd buffer");
11888            assert_eq!(
11889                active_item.item_id(),
11890                multibuffer_item_id,
11891                "Should navigate back from the 2nd buffer to the multi buffer"
11892            );
11893            assert!(!active_item.is_singleton(cx));
11894        })
11895        .unwrap();
11896
11897    multi_buffer_editor.update(cx, |editor, cx| {
11898        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11899            s.select_ranges(Some(70..70))
11900        });
11901        editor.open_excerpts(&OpenExcerpts, cx);
11902    });
11903    cx.executor().run_until_parked();
11904    workspace
11905        .update(cx, |workspace, cx| {
11906            let active_item = workspace
11907                .active_item(cx)
11908                .expect("should have an active item after navigating into the 3rd buffer");
11909            let third_item_id = active_item.item_id();
11910            assert_ne!(
11911                third_item_id, multibuffer_item_id,
11912                "Should navigate into the 3rd buffer and activate it"
11913            );
11914            assert_ne!(third_item_id, first_item_id);
11915            assert_ne!(third_item_id, second_item_id);
11916            assert!(
11917                active_item.is_singleton(cx),
11918                "New active item should be a singleton buffer"
11919            );
11920            assert_eq!(
11921                active_item
11922                    .act_as::<Editor>(cx)
11923                    .expect("should have navigated into an editor")
11924                    .read(cx)
11925                    .text(cx),
11926                sample_text_3
11927            );
11928
11929            workspace
11930                .go_back(workspace.active_pane().downgrade(), cx)
11931                .detach_and_log_err(cx);
11932        })
11933        .unwrap();
11934    cx.executor().run_until_parked();
11935    workspace
11936        .update(cx, |workspace, cx| {
11937            let active_item = workspace
11938                .active_item(cx)
11939                .expect("should have an active item after navigating back from the 3rd buffer");
11940            assert_eq!(
11941                active_item.item_id(),
11942                multibuffer_item_id,
11943                "Should navigate back from the 3rd buffer to the multi buffer"
11944            );
11945            assert!(!active_item.is_singleton(cx));
11946        })
11947        .unwrap();
11948}
11949
11950#[gpui::test]
11951async fn test_multibuffer_unfold_on_jump(cx: &mut gpui::TestAppContext) {
11952    init_test(cx, |_| {});
11953
11954    let texts = ["{\n\tx\n}".to_owned(), "y".to_owned()];
11955    let buffers = texts
11956        .clone()
11957        .map(|txt| cx.new_model(|cx| Buffer::local(txt, cx)));
11958    let multi_buffer = cx.new_model(|cx| {
11959        let mut multi_buffer = MultiBuffer::new(ReadWrite);
11960        for i in 0..2 {
11961            multi_buffer.push_excerpts(
11962                buffers[i].clone(),
11963                [ExcerptRange {
11964                    context: 0..texts[i].len(),
11965                    primary: None,
11966                }],
11967                cx,
11968            );
11969        }
11970        multi_buffer
11971    });
11972
11973    let fs = FakeFs::new(cx.executor());
11974    fs.insert_tree(
11975        "/project",
11976        json!({
11977            "x": &texts[0],
11978            "y": &texts[1],
11979        }),
11980    )
11981    .await;
11982    let project = Project::test(fs, ["/project".as_ref()], cx).await;
11983    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11984    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11985
11986    let multi_buffer_editor = cx.new_view(|cx| {
11987        Editor::for_multibuffer(multi_buffer.clone(), Some(project.clone()), true, cx)
11988    });
11989    let buffer_editor =
11990        cx.new_view(|cx| Editor::for_buffer(buffers[0].clone(), Some(project.clone()), cx));
11991    workspace
11992        .update(cx, |workspace, cx| {
11993            workspace.add_item_to_active_pane(
11994                Box::new(multi_buffer_editor.clone()),
11995                None,
11996                true,
11997                cx,
11998            );
11999            workspace.add_item_to_active_pane(Box::new(buffer_editor.clone()), None, false, cx);
12000        })
12001        .unwrap();
12002    cx.executor().run_until_parked();
12003    buffer_editor.update(cx, |buffer_editor, cx| {
12004        buffer_editor.fold_at_level(&FoldAtLevel { level: 1 }, cx);
12005        assert!(buffer_editor.snapshot(cx).fold_count() == 1);
12006    });
12007    cx.executor().run_until_parked();
12008    multi_buffer_editor.update(cx, |multi_buffer_editor, cx| {
12009        multi_buffer_editor.change_selections(None, cx, |s| s.select_ranges([3..4]));
12010        multi_buffer_editor.open_excerpts(&OpenExcerpts, cx);
12011    });
12012    cx.executor().run_until_parked();
12013    buffer_editor.update(cx, |buffer_editor, cx| {
12014        assert!(buffer_editor.snapshot(cx).fold_count() == 0);
12015    });
12016}
12017
12018#[gpui::test]
12019async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12020    init_test(cx, |_| {});
12021
12022    let mut cx = EditorTestContext::new(cx).await;
12023
12024    let diff_base = r#"
12025        use some::mod;
12026
12027        const A: u32 = 42;
12028
12029        fn main() {
12030            println!("hello");
12031
12032            println!("world");
12033        }
12034        "#
12035    .unindent();
12036
12037    cx.set_state(
12038        &r#"
12039        use some::modified;
12040
12041        ˇ
12042        fn main() {
12043            println!("hello there");
12044
12045            println!("around the");
12046            println!("world");
12047        }
12048        "#
12049        .unindent(),
12050    );
12051
12052    cx.set_diff_base(Some(&diff_base));
12053    executor.run_until_parked();
12054
12055    cx.update_editor(|editor, cx| {
12056        editor.go_to_next_hunk(&GoToHunk, cx);
12057        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12058    });
12059    executor.run_until_parked();
12060    cx.assert_diff_hunks(
12061        r#"
12062          use some::modified;
12063
12064
12065          fn main() {
12066        -     println!("hello");
12067        +     println!("hello there");
12068
12069              println!("around the");
12070              println!("world");
12071          }
12072        "#
12073        .unindent(),
12074    );
12075
12076    cx.update_editor(|editor, cx| {
12077        for _ in 0..3 {
12078            editor.go_to_next_hunk(&GoToHunk, cx);
12079            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12080        }
12081    });
12082    executor.run_until_parked();
12083    cx.assert_editor_state(
12084        &r#"
12085        use some::modified;
12086
12087        ˇ
12088        fn main() {
12089            println!("hello there");
12090
12091            println!("around the");
12092            println!("world");
12093        }
12094        "#
12095        .unindent(),
12096    );
12097
12098    cx.assert_diff_hunks(
12099        r#"
12100        - use some::mod;
12101        + use some::modified;
12102
12103        - const A: u32 = 42;
12104
12105          fn main() {
12106        -     println!("hello");
12107        +     println!("hello there");
12108
12109        +     println!("around the");
12110              println!("world");
12111          }
12112        "#
12113        .unindent(),
12114    );
12115
12116    cx.update_editor(|editor, cx| {
12117        editor.cancel(&Cancel, cx);
12118    });
12119
12120    cx.assert_diff_hunks(
12121        r#"
12122          use some::modified;
12123
12124
12125          fn main() {
12126              println!("hello there");
12127
12128              println!("around the");
12129              println!("world");
12130          }
12131        "#
12132        .unindent(),
12133    );
12134}
12135
12136#[gpui::test]
12137async fn test_diff_base_change_with_expanded_diff_hunks(
12138    executor: BackgroundExecutor,
12139    cx: &mut gpui::TestAppContext,
12140) {
12141    init_test(cx, |_| {});
12142
12143    let mut cx = EditorTestContext::new(cx).await;
12144
12145    let diff_base = r#"
12146        use some::mod1;
12147        use some::mod2;
12148
12149        const A: u32 = 42;
12150        const B: u32 = 42;
12151        const C: u32 = 42;
12152
12153        fn main() {
12154            println!("hello");
12155
12156            println!("world");
12157        }
12158        "#
12159    .unindent();
12160
12161    cx.set_state(
12162        &r#"
12163        use some::mod2;
12164
12165        const A: u32 = 42;
12166        const C: u32 = 42;
12167
12168        fn main(ˇ) {
12169            //println!("hello");
12170
12171            println!("world");
12172            //
12173            //
12174        }
12175        "#
12176        .unindent(),
12177    );
12178
12179    cx.set_diff_base(Some(&diff_base));
12180    executor.run_until_parked();
12181
12182    cx.update_editor(|editor, cx| {
12183        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12184    });
12185    executor.run_until_parked();
12186    cx.assert_diff_hunks(
12187        r#"
12188        - use some::mod1;
12189          use some::mod2;
12190
12191          const A: u32 = 42;
12192        - const B: u32 = 42;
12193          const C: u32 = 42;
12194
12195          fn main() {
12196        -     println!("hello");
12197        +     //println!("hello");
12198
12199              println!("world");
12200        +     //
12201        +     //
12202          }
12203        "#
12204        .unindent(),
12205    );
12206
12207    cx.set_diff_base(Some("new diff base!"));
12208    executor.run_until_parked();
12209    cx.assert_diff_hunks(
12210        r#"
12211          use some::mod2;
12212
12213          const A: u32 = 42;
12214          const C: u32 = 42;
12215
12216          fn main() {
12217              //println!("hello");
12218
12219              println!("world");
12220              //
12221              //
12222          }
12223        "#
12224        .unindent(),
12225    );
12226
12227    cx.update_editor(|editor, cx| {
12228        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12229    });
12230    executor.run_until_parked();
12231    cx.assert_diff_hunks(
12232        r#"
12233        - new diff base!
12234        + use some::mod2;
12235        +
12236        + const A: u32 = 42;
12237        + const C: u32 = 42;
12238        +
12239        + fn main() {
12240        +     //println!("hello");
12241        +
12242        +     println!("world");
12243        +     //
12244        +     //
12245        + }
12246        "#
12247        .unindent(),
12248    );
12249}
12250
12251#[gpui::test]
12252async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12253    init_test(cx, |_| {});
12254
12255    let mut cx = EditorTestContext::new(cx).await;
12256
12257    let diff_base = r#"
12258        use some::mod1;
12259        use some::mod2;
12260
12261        const A: u32 = 42;
12262        const B: u32 = 42;
12263        const C: u32 = 42;
12264
12265        fn main() {
12266            println!("hello");
12267
12268            println!("world");
12269        }
12270
12271        fn another() {
12272            println!("another");
12273        }
12274
12275        fn another2() {
12276            println!("another2");
12277        }
12278        "#
12279    .unindent();
12280
12281    cx.set_state(
12282        &r#"
12283        «use some::mod2;
12284
12285        const A: u32 = 42;
12286        const C: u32 = 42;
12287
12288        fn main() {
12289            //println!("hello");
12290
12291            println!("world");
12292            //
12293            //ˇ»
12294        }
12295
12296        fn another() {
12297            println!("another");
12298            println!("another");
12299        }
12300
12301            println!("another2");
12302        }
12303        "#
12304        .unindent(),
12305    );
12306
12307    cx.set_diff_base(Some(&diff_base));
12308    executor.run_until_parked();
12309
12310    cx.update_editor(|editor, cx| {
12311        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12312    });
12313    executor.run_until_parked();
12314
12315    cx.assert_diff_hunks(
12316        r#"
12317        - use some::mod1;
12318          use some::mod2;
12319
12320          const A: u32 = 42;
12321        - const B: u32 = 42;
12322          const C: u32 = 42;
12323
12324          fn main() {
12325        -     println!("hello");
12326        +     //println!("hello");
12327
12328              println!("world");
12329        +     //
12330        +     //
12331          }
12332
12333          fn another() {
12334              println!("another");
12335        +     println!("another");
12336          }
12337
12338        - fn another2() {
12339              println!("another2");
12340          }
12341        "#
12342        .unindent(),
12343    );
12344
12345    // Fold across some of the diff hunks. They should no longer appear expanded.
12346    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12347    cx.executor().run_until_parked();
12348
12349    // Hunks are not shown if their position is within a fold
12350    cx.assert_diff_hunks(
12351        r#"
12352          use some::mod2;
12353
12354          const A: u32 = 42;
12355          const C: u32 = 42;
12356
12357          fn main() {
12358              //println!("hello");
12359
12360              println!("world");
12361              //
12362              //
12363          }
12364
12365          fn another() {
12366              println!("another");
12367        +     println!("another");
12368          }
12369
12370        - fn another2() {
12371              println!("another2");
12372          }
12373        "#
12374        .unindent(),
12375    );
12376
12377    cx.update_editor(|editor, cx| {
12378        editor.select_all(&SelectAll, cx);
12379        editor.unfold_lines(&UnfoldLines, cx);
12380    });
12381    cx.executor().run_until_parked();
12382
12383    // The deletions reappear when unfolding.
12384    cx.assert_diff_hunks(
12385        r#"
12386        - use some::mod1;
12387          use some::mod2;
12388
12389          const A: u32 = 42;
12390        - const B: u32 = 42;
12391          const C: u32 = 42;
12392
12393          fn main() {
12394        -     println!("hello");
12395        +     //println!("hello");
12396
12397              println!("world");
12398        +     //
12399        +     //
12400          }
12401
12402          fn another() {
12403              println!("another");
12404        +     println!("another");
12405          }
12406
12407        - fn another2() {
12408              println!("another2");
12409          }
12410        "#
12411        .unindent(),
12412    );
12413}
12414
12415#[gpui::test]
12416async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12417    init_test(cx, |_| {});
12418
12419    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12420    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12421    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12422    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12423    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12424    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12425
12426    let buffer_1 = cx.new_model(|cx| {
12427        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
12428        buffer.set_diff_base(Some(file_1_old.into()), cx);
12429        buffer
12430    });
12431    let buffer_2 = cx.new_model(|cx| {
12432        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
12433        buffer.set_diff_base(Some(file_2_old.into()), cx);
12434        buffer
12435    });
12436    let buffer_3 = cx.new_model(|cx| {
12437        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
12438        buffer.set_diff_base(Some(file_3_old.into()), cx);
12439        buffer
12440    });
12441
12442    let multi_buffer = cx.new_model(|cx| {
12443        let mut multibuffer = MultiBuffer::new(ReadWrite);
12444        multibuffer.push_excerpts(
12445            buffer_1.clone(),
12446            [
12447                ExcerptRange {
12448                    context: Point::new(0, 0)..Point::new(3, 0),
12449                    primary: None,
12450                },
12451                ExcerptRange {
12452                    context: Point::new(5, 0)..Point::new(7, 0),
12453                    primary: None,
12454                },
12455                ExcerptRange {
12456                    context: Point::new(9, 0)..Point::new(10, 3),
12457                    primary: None,
12458                },
12459            ],
12460            cx,
12461        );
12462        multibuffer.push_excerpts(
12463            buffer_2.clone(),
12464            [
12465                ExcerptRange {
12466                    context: Point::new(0, 0)..Point::new(3, 0),
12467                    primary: None,
12468                },
12469                ExcerptRange {
12470                    context: Point::new(5, 0)..Point::new(7, 0),
12471                    primary: None,
12472                },
12473                ExcerptRange {
12474                    context: Point::new(9, 0)..Point::new(10, 3),
12475                    primary: None,
12476                },
12477            ],
12478            cx,
12479        );
12480        multibuffer.push_excerpts(
12481            buffer_3.clone(),
12482            [
12483                ExcerptRange {
12484                    context: Point::new(0, 0)..Point::new(3, 0),
12485                    primary: None,
12486                },
12487                ExcerptRange {
12488                    context: Point::new(5, 0)..Point::new(7, 0),
12489                    primary: None,
12490                },
12491                ExcerptRange {
12492                    context: Point::new(9, 0)..Point::new(10, 3),
12493                    primary: None,
12494                },
12495            ],
12496            cx,
12497        );
12498        multibuffer
12499    });
12500
12501    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12502    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12503    cx.run_until_parked();
12504
12505    cx.assert_editor_state(
12506        &"
12507            ˇaaa
12508            ccc
12509            ddd
12510
12511            ggg
12512            hhh
12513
12514
12515            lll
12516            mmm
12517            NNN
12518
12519            qqq
12520            rrr
12521
12522            uuu
12523            111
12524            222
12525            333
12526
12527            666
12528            777
12529
12530            000
12531            !!!"
12532        .unindent(),
12533    );
12534
12535    cx.update_editor(|editor, cx| {
12536        editor.select_all(&SelectAll, cx);
12537        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12538    });
12539    cx.executor().run_until_parked();
12540
12541    cx.assert_diff_hunks(
12542        "
12543            aaa
12544          - bbb
12545            ccc
12546            ddd
12547
12548            ggg
12549            hhh
12550
12551
12552            lll
12553            mmm
12554          - nnn
12555          + NNN
12556
12557            qqq
12558            rrr
12559
12560            uuu
12561            111
12562            222
12563            333
12564
12565          + 666
12566            777
12567
12568            000
12569            !!!"
12570        .unindent(),
12571    );
12572}
12573
12574#[gpui::test]
12575async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12576    init_test(cx, |_| {});
12577
12578    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12579    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12580
12581    let buffer = cx.new_model(|cx| {
12582        let mut buffer = Buffer::local(text.to_string(), cx);
12583        buffer.set_diff_base(Some(base.into()), cx);
12584        buffer
12585    });
12586
12587    let multi_buffer = cx.new_model(|cx| {
12588        let mut multibuffer = MultiBuffer::new(ReadWrite);
12589        multibuffer.push_excerpts(
12590            buffer.clone(),
12591            [
12592                ExcerptRange {
12593                    context: Point::new(0, 0)..Point::new(2, 0),
12594                    primary: None,
12595                },
12596                ExcerptRange {
12597                    context: Point::new(5, 0)..Point::new(7, 0),
12598                    primary: None,
12599                },
12600            ],
12601            cx,
12602        );
12603        multibuffer
12604    });
12605
12606    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12607    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12608    cx.run_until_parked();
12609
12610    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12611    cx.executor().run_until_parked();
12612
12613    cx.assert_diff_hunks(
12614        "
12615            aaa
12616          - bbb
12617          + BBB
12618
12619          - ddd
12620          - eee
12621          + EEE
12622            fff
12623        "
12624        .unindent(),
12625    );
12626}
12627
12628#[gpui::test]
12629async fn test_edits_around_expanded_insertion_hunks(
12630    executor: BackgroundExecutor,
12631    cx: &mut gpui::TestAppContext,
12632) {
12633    init_test(cx, |_| {});
12634
12635    let mut cx = EditorTestContext::new(cx).await;
12636
12637    let diff_base = r#"
12638        use some::mod1;
12639        use some::mod2;
12640
12641        const A: u32 = 42;
12642
12643        fn main() {
12644            println!("hello");
12645
12646            println!("world");
12647        }
12648        "#
12649    .unindent();
12650    executor.run_until_parked();
12651    cx.set_state(
12652        &r#"
12653        use some::mod1;
12654        use some::mod2;
12655
12656        const A: u32 = 42;
12657        const B: u32 = 42;
12658        const C: u32 = 42;
12659        ˇ
12660
12661        fn main() {
12662            println!("hello");
12663
12664            println!("world");
12665        }
12666        "#
12667        .unindent(),
12668    );
12669
12670    cx.set_diff_base(Some(&diff_base));
12671    executor.run_until_parked();
12672
12673    cx.update_editor(|editor, cx| {
12674        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12675    });
12676    executor.run_until_parked();
12677
12678    cx.assert_diff_hunks(
12679        r#"
12680        use some::mod1;
12681        use some::mod2;
12682
12683        const A: u32 = 42;
12684      + const B: u32 = 42;
12685      + const C: u32 = 42;
12686      +
12687
12688        fn main() {
12689            println!("hello");
12690
12691            println!("world");
12692        }
12693        "#
12694        .unindent(),
12695    );
12696
12697    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12698    executor.run_until_parked();
12699
12700    cx.assert_diff_hunks(
12701        r#"
12702        use some::mod1;
12703        use some::mod2;
12704
12705        const A: u32 = 42;
12706      + const B: u32 = 42;
12707      + const C: u32 = 42;
12708      + const D: u32 = 42;
12709      +
12710
12711        fn main() {
12712            println!("hello");
12713
12714            println!("world");
12715        }
12716        "#
12717        .unindent(),
12718    );
12719
12720    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12721    executor.run_until_parked();
12722
12723    cx.assert_diff_hunks(
12724        r#"
12725        use some::mod1;
12726        use some::mod2;
12727
12728        const A: u32 = 42;
12729      + const B: u32 = 42;
12730      + const C: u32 = 42;
12731      + const D: u32 = 42;
12732      + const E: u32 = 42;
12733      +
12734
12735        fn main() {
12736            println!("hello");
12737
12738            println!("world");
12739        }
12740        "#
12741        .unindent(),
12742    );
12743
12744    cx.update_editor(|editor, cx| {
12745        editor.delete_line(&DeleteLine, cx);
12746    });
12747    executor.run_until_parked();
12748
12749    cx.assert_diff_hunks(
12750        r#"
12751        use some::mod1;
12752        use some::mod2;
12753
12754        const A: u32 = 42;
12755      + const B: u32 = 42;
12756      + const C: u32 = 42;
12757      + const D: u32 = 42;
12758      + const E: u32 = 42;
12759
12760        fn main() {
12761            println!("hello");
12762
12763            println!("world");
12764        }
12765        "#
12766        .unindent(),
12767    );
12768
12769    cx.update_editor(|editor, cx| {
12770        editor.move_up(&MoveUp, cx);
12771        editor.delete_line(&DeleteLine, cx);
12772        editor.move_up(&MoveUp, cx);
12773        editor.delete_line(&DeleteLine, cx);
12774        editor.move_up(&MoveUp, cx);
12775        editor.delete_line(&DeleteLine, cx);
12776    });
12777    executor.run_until_parked();
12778    cx.assert_editor_state(
12779        &r#"
12780        use some::mod1;
12781        use some::mod2;
12782
12783        const A: u32 = 42;
12784        const B: u32 = 42;
12785        ˇ
12786        fn main() {
12787            println!("hello");
12788
12789            println!("world");
12790        }
12791        "#
12792        .unindent(),
12793    );
12794
12795    cx.assert_diff_hunks(
12796        r#"
12797        use some::mod1;
12798        use some::mod2;
12799
12800        const A: u32 = 42;
12801      + const B: u32 = 42;
12802
12803        fn main() {
12804            println!("hello");
12805
12806            println!("world");
12807        }
12808        "#
12809        .unindent(),
12810    );
12811
12812    cx.update_editor(|editor, cx| {
12813        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12814        editor.delete_line(&DeleteLine, cx);
12815    });
12816    executor.run_until_parked();
12817    cx.assert_diff_hunks(
12818        r#"
12819        use some::mod1;
12820      - use some::mod2;
12821      -
12822      - const A: u32 = 42;
12823
12824        fn main() {
12825            println!("hello");
12826
12827            println!("world");
12828        }
12829        "#
12830        .unindent(),
12831    );
12832}
12833
12834#[gpui::test]
12835async fn test_edits_around_expanded_deletion_hunks(
12836    executor: BackgroundExecutor,
12837    cx: &mut gpui::TestAppContext,
12838) {
12839    init_test(cx, |_| {});
12840
12841    let mut cx = EditorTestContext::new(cx).await;
12842
12843    let diff_base = 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
12852        fn main() {
12853            println!("hello");
12854
12855            println!("world");
12856        }
12857    "#
12858    .unindent();
12859    executor.run_until_parked();
12860    cx.set_state(
12861        &r#"
12862        use some::mod1;
12863        use some::mod2;
12864
12865        ˇconst B: u32 = 42;
12866        const C: u32 = 42;
12867
12868
12869        fn main() {
12870            println!("hello");
12871
12872            println!("world");
12873        }
12874        "#
12875        .unindent(),
12876    );
12877
12878    cx.set_diff_base(Some(&diff_base));
12879    executor.run_until_parked();
12880
12881    cx.update_editor(|editor, cx| {
12882        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12883    });
12884    executor.run_until_parked();
12885
12886    cx.assert_diff_hunks(
12887        r#"
12888        use some::mod1;
12889        use some::mod2;
12890
12891      - const A: u32 = 42;
12892        const B: u32 = 42;
12893        const C: u32 = 42;
12894
12895
12896        fn main() {
12897            println!("hello");
12898
12899            println!("world");
12900        }
12901        "#
12902        .unindent(),
12903    );
12904
12905    cx.update_editor(|editor, cx| {
12906        editor.delete_line(&DeleteLine, cx);
12907    });
12908    executor.run_until_parked();
12909    cx.assert_editor_state(
12910        &r#"
12911        use some::mod1;
12912        use some::mod2;
12913
12914        ˇconst C: u32 = 42;
12915
12916
12917        fn main() {
12918            println!("hello");
12919
12920            println!("world");
12921        }
12922        "#
12923        .unindent(),
12924    );
12925    cx.assert_diff_hunks(
12926        r#"
12927        use some::mod1;
12928        use some::mod2;
12929
12930      - const A: u32 = 42;
12931      - const B: u32 = 42;
12932        const C: u32 = 42;
12933
12934
12935        fn main() {
12936            println!("hello");
12937
12938            println!("world");
12939        }
12940        "#
12941        .unindent(),
12942    );
12943
12944    cx.update_editor(|editor, cx| {
12945        editor.delete_line(&DeleteLine, cx);
12946    });
12947    executor.run_until_parked();
12948    cx.assert_editor_state(
12949        &r#"
12950        use some::mod1;
12951        use some::mod2;
12952
12953        ˇ
12954
12955        fn main() {
12956            println!("hello");
12957
12958            println!("world");
12959        }
12960        "#
12961        .unindent(),
12962    );
12963    cx.assert_diff_hunks(
12964        r#"
12965        use some::mod1;
12966        use some::mod2;
12967
12968      - const A: u32 = 42;
12969      - const B: u32 = 42;
12970      - const C: u32 = 42;
12971
12972
12973        fn main() {
12974            println!("hello");
12975
12976            println!("world");
12977        }
12978        "#
12979        .unindent(),
12980    );
12981
12982    cx.update_editor(|editor, cx| {
12983        editor.handle_input("replacement", cx);
12984    });
12985    executor.run_until_parked();
12986    cx.assert_editor_state(
12987        &r#"
12988        use some::mod1;
12989        use some::mod2;
12990
12991        replacementˇ
12992
12993        fn main() {
12994            println!("hello");
12995
12996            println!("world");
12997        }
12998        "#
12999        .unindent(),
13000    );
13001    cx.assert_diff_hunks(
13002        r#"
13003        use some::mod1;
13004        use some::mod2;
13005
13006      - const A: u32 = 42;
13007      - const B: u32 = 42;
13008      - const C: u32 = 42;
13009      -
13010      + replacement
13011
13012        fn main() {
13013            println!("hello");
13014
13015            println!("world");
13016        }
13017        "#
13018        .unindent(),
13019    );
13020}
13021
13022#[gpui::test]
13023async fn test_edit_after_expanded_modification_hunk(
13024    executor: BackgroundExecutor,
13025    cx: &mut gpui::TestAppContext,
13026) {
13027    init_test(cx, |_| {});
13028
13029    let mut cx = EditorTestContext::new(cx).await;
13030
13031    let diff_base = r#"
13032        use some::mod1;
13033        use some::mod2;
13034
13035        const A: u32 = 42;
13036        const B: u32 = 42;
13037        const C: u32 = 42;
13038        const D: u32 = 42;
13039
13040
13041        fn main() {
13042            println!("hello");
13043
13044            println!("world");
13045        }"#
13046    .unindent();
13047
13048    cx.set_state(
13049        &r#"
13050        use some::mod1;
13051        use some::mod2;
13052
13053        const A: u32 = 42;
13054        const B: u32 = 42;
13055        const C: u32 = 43ˇ
13056        const D: u32 = 42;
13057
13058
13059        fn main() {
13060            println!("hello");
13061
13062            println!("world");
13063        }"#
13064        .unindent(),
13065    );
13066
13067    cx.set_diff_base(Some(&diff_base));
13068    executor.run_until_parked();
13069    cx.update_editor(|editor, cx| {
13070        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13071    });
13072    executor.run_until_parked();
13073
13074    cx.assert_diff_hunks(
13075        r#"
13076        use some::mod1;
13077        use some::mod2;
13078
13079        const A: u32 = 42;
13080        const B: u32 = 42;
13081      - const C: u32 = 42;
13082      + const C: u32 = 43
13083        const D: u32 = 42;
13084
13085
13086        fn main() {
13087            println!("hello");
13088
13089            println!("world");
13090        }"#
13091        .unindent(),
13092    );
13093
13094    cx.update_editor(|editor, cx| {
13095        editor.handle_input("\nnew_line\n", cx);
13096    });
13097    executor.run_until_parked();
13098
13099    cx.assert_diff_hunks(
13100        r#"
13101        use some::mod1;
13102        use some::mod2;
13103
13104        const A: u32 = 42;
13105        const B: u32 = 42;
13106      - const C: u32 = 42;
13107      + const C: u32 = 43
13108      + new_line
13109      +
13110        const D: u32 = 42;
13111
13112
13113        fn main() {
13114            println!("hello");
13115
13116            println!("world");
13117        }"#
13118        .unindent(),
13119    );
13120}
13121
13122async fn setup_indent_guides_editor(
13123    text: &str,
13124    cx: &mut gpui::TestAppContext,
13125) -> (BufferId, EditorTestContext) {
13126    init_test(cx, |_| {});
13127
13128    let mut cx = EditorTestContext::new(cx).await;
13129
13130    let buffer_id = cx.update_editor(|editor, cx| {
13131        editor.set_text(text, cx);
13132        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13133
13134        buffer_ids[0]
13135    });
13136
13137    (buffer_id, cx)
13138}
13139
13140fn assert_indent_guides(
13141    range: Range<u32>,
13142    expected: Vec<IndentGuide>,
13143    active_indices: Option<Vec<usize>>,
13144    cx: &mut EditorTestContext,
13145) {
13146    let indent_guides = cx.update_editor(|editor, cx| {
13147        let snapshot = editor.snapshot(cx).display_snapshot;
13148        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13149            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13150            true,
13151            &snapshot,
13152            cx,
13153        );
13154
13155        indent_guides.sort_by(|a, b| {
13156            a.depth.cmp(&b.depth).then(
13157                a.start_row
13158                    .cmp(&b.start_row)
13159                    .then(a.end_row.cmp(&b.end_row)),
13160            )
13161        });
13162        indent_guides
13163    });
13164
13165    if let Some(expected) = active_indices {
13166        let active_indices = cx.update_editor(|editor, cx| {
13167            let snapshot = editor.snapshot(cx).display_snapshot;
13168            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13169        });
13170
13171        assert_eq!(
13172            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13173            expected,
13174            "Active indent guide indices do not match"
13175        );
13176    }
13177
13178    let expected: Vec<_> = expected
13179        .into_iter()
13180        .map(|guide| MultiBufferIndentGuide {
13181            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13182            buffer: guide,
13183        })
13184        .collect();
13185
13186    assert_eq!(indent_guides, expected, "Indent guides do not match");
13187}
13188
13189fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13190    IndentGuide {
13191        buffer_id,
13192        start_row,
13193        end_row,
13194        depth,
13195        tab_size: 4,
13196        settings: IndentGuideSettings {
13197            enabled: true,
13198            line_width: 1,
13199            active_line_width: 1,
13200            ..Default::default()
13201        },
13202    }
13203}
13204
13205#[gpui::test]
13206async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13207    let (buffer_id, mut cx) = setup_indent_guides_editor(
13208        &"
13209    fn main() {
13210        let a = 1;
13211    }"
13212        .unindent(),
13213        cx,
13214    )
13215    .await;
13216
13217    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13218}
13219
13220#[gpui::test]
13221async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13222    let (buffer_id, mut cx) = setup_indent_guides_editor(
13223        &"
13224    fn main() {
13225        let a = 1;
13226        let b = 2;
13227    }"
13228        .unindent(),
13229        cx,
13230    )
13231    .await;
13232
13233    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13234}
13235
13236#[gpui::test]
13237async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13238    let (buffer_id, mut cx) = setup_indent_guides_editor(
13239        &"
13240    fn main() {
13241        let a = 1;
13242        if a == 3 {
13243            let b = 2;
13244        } else {
13245            let c = 3;
13246        }
13247    }"
13248        .unindent(),
13249        cx,
13250    )
13251    .await;
13252
13253    assert_indent_guides(
13254        0..8,
13255        vec![
13256            indent_guide(buffer_id, 1, 6, 0),
13257            indent_guide(buffer_id, 3, 3, 1),
13258            indent_guide(buffer_id, 5, 5, 1),
13259        ],
13260        None,
13261        &mut cx,
13262    );
13263}
13264
13265#[gpui::test]
13266async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13267    let (buffer_id, mut cx) = setup_indent_guides_editor(
13268        &"
13269    fn main() {
13270        let a = 1;
13271            let b = 2;
13272        let c = 3;
13273    }"
13274        .unindent(),
13275        cx,
13276    )
13277    .await;
13278
13279    assert_indent_guides(
13280        0..5,
13281        vec![
13282            indent_guide(buffer_id, 1, 3, 0),
13283            indent_guide(buffer_id, 2, 2, 1),
13284        ],
13285        None,
13286        &mut cx,
13287    );
13288}
13289
13290#[gpui::test]
13291async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13292    let (buffer_id, mut cx) = setup_indent_guides_editor(
13293        &"
13294        fn main() {
13295            let a = 1;
13296
13297            let c = 3;
13298        }"
13299        .unindent(),
13300        cx,
13301    )
13302    .await;
13303
13304    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13305}
13306
13307#[gpui::test]
13308async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13309    let (buffer_id, mut cx) = setup_indent_guides_editor(
13310        &"
13311        fn main() {
13312            let a = 1;
13313
13314            let c = 3;
13315
13316            if a == 3 {
13317                let b = 2;
13318            } else {
13319                let c = 3;
13320            }
13321        }"
13322        .unindent(),
13323        cx,
13324    )
13325    .await;
13326
13327    assert_indent_guides(
13328        0..11,
13329        vec![
13330            indent_guide(buffer_id, 1, 9, 0),
13331            indent_guide(buffer_id, 6, 6, 1),
13332            indent_guide(buffer_id, 8, 8, 1),
13333        ],
13334        None,
13335        &mut cx,
13336    );
13337}
13338
13339#[gpui::test]
13340async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13341    let (buffer_id, mut cx) = setup_indent_guides_editor(
13342        &"
13343        fn main() {
13344            let a = 1;
13345
13346            let c = 3;
13347
13348            if a == 3 {
13349                let b = 2;
13350            } else {
13351                let c = 3;
13352            }
13353        }"
13354        .unindent(),
13355        cx,
13356    )
13357    .await;
13358
13359    assert_indent_guides(
13360        1..11,
13361        vec![
13362            indent_guide(buffer_id, 1, 9, 0),
13363            indent_guide(buffer_id, 6, 6, 1),
13364            indent_guide(buffer_id, 8, 8, 1),
13365        ],
13366        None,
13367        &mut cx,
13368    );
13369}
13370
13371#[gpui::test]
13372async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13373    let (buffer_id, mut cx) = setup_indent_guides_editor(
13374        &"
13375        fn main() {
13376            let a = 1;
13377
13378            let c = 3;
13379
13380            if a == 3 {
13381                let b = 2;
13382            } else {
13383                let c = 3;
13384            }
13385        }"
13386        .unindent(),
13387        cx,
13388    )
13389    .await;
13390
13391    assert_indent_guides(
13392        1..10,
13393        vec![
13394            indent_guide(buffer_id, 1, 9, 0),
13395            indent_guide(buffer_id, 6, 6, 1),
13396            indent_guide(buffer_id, 8, 8, 1),
13397        ],
13398        None,
13399        &mut cx,
13400    );
13401}
13402
13403#[gpui::test]
13404async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13405    let (buffer_id, mut cx) = setup_indent_guides_editor(
13406        &"
13407        block1
13408            block2
13409                block3
13410                    block4
13411            block2
13412        block1
13413        block1"
13414            .unindent(),
13415        cx,
13416    )
13417    .await;
13418
13419    assert_indent_guides(
13420        1..10,
13421        vec![
13422            indent_guide(buffer_id, 1, 4, 0),
13423            indent_guide(buffer_id, 2, 3, 1),
13424            indent_guide(buffer_id, 3, 3, 2),
13425        ],
13426        None,
13427        &mut cx,
13428    );
13429}
13430
13431#[gpui::test]
13432async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13433    let (buffer_id, mut cx) = setup_indent_guides_editor(
13434        &"
13435        block1
13436            block2
13437                block3
13438
13439        block1
13440        block1"
13441            .unindent(),
13442        cx,
13443    )
13444    .await;
13445
13446    assert_indent_guides(
13447        0..6,
13448        vec![
13449            indent_guide(buffer_id, 1, 2, 0),
13450            indent_guide(buffer_id, 2, 2, 1),
13451        ],
13452        None,
13453        &mut cx,
13454    );
13455}
13456
13457#[gpui::test]
13458async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13459    let (buffer_id, mut cx) = setup_indent_guides_editor(
13460        &"
13461        block1
13462
13463
13464
13465            block2
13466        "
13467        .unindent(),
13468        cx,
13469    )
13470    .await;
13471
13472    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13473}
13474
13475#[gpui::test]
13476async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13477    let (buffer_id, mut cx) = setup_indent_guides_editor(
13478        &"
13479        def a:
13480        \tb = 3
13481        \tif True:
13482        \t\tc = 4
13483        \t\td = 5
13484        \tprint(b)
13485        "
13486        .unindent(),
13487        cx,
13488    )
13489    .await;
13490
13491    assert_indent_guides(
13492        0..6,
13493        vec![
13494            indent_guide(buffer_id, 1, 6, 0),
13495            indent_guide(buffer_id, 3, 4, 1),
13496        ],
13497        None,
13498        &mut cx,
13499    );
13500}
13501
13502#[gpui::test]
13503async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13504    let (buffer_id, mut cx) = setup_indent_guides_editor(
13505        &"
13506    fn main() {
13507        let a = 1;
13508    }"
13509        .unindent(),
13510        cx,
13511    )
13512    .await;
13513
13514    cx.update_editor(|editor, cx| {
13515        editor.change_selections(None, cx, |s| {
13516            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13517        });
13518    });
13519
13520    assert_indent_guides(
13521        0..3,
13522        vec![indent_guide(buffer_id, 1, 1, 0)],
13523        Some(vec![0]),
13524        &mut cx,
13525    );
13526}
13527
13528#[gpui::test]
13529async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13530    let (buffer_id, mut cx) = setup_indent_guides_editor(
13531        &"
13532    fn main() {
13533        if 1 == 2 {
13534            let a = 1;
13535        }
13536    }"
13537        .unindent(),
13538        cx,
13539    )
13540    .await;
13541
13542    cx.update_editor(|editor, cx| {
13543        editor.change_selections(None, cx, |s| {
13544            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13545        });
13546    });
13547
13548    assert_indent_guides(
13549        0..4,
13550        vec![
13551            indent_guide(buffer_id, 1, 3, 0),
13552            indent_guide(buffer_id, 2, 2, 1),
13553        ],
13554        Some(vec![1]),
13555        &mut cx,
13556    );
13557
13558    cx.update_editor(|editor, cx| {
13559        editor.change_selections(None, cx, |s| {
13560            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13561        });
13562    });
13563
13564    assert_indent_guides(
13565        0..4,
13566        vec![
13567            indent_guide(buffer_id, 1, 3, 0),
13568            indent_guide(buffer_id, 2, 2, 1),
13569        ],
13570        Some(vec![1]),
13571        &mut cx,
13572    );
13573
13574    cx.update_editor(|editor, cx| {
13575        editor.change_selections(None, cx, |s| {
13576            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13577        });
13578    });
13579
13580    assert_indent_guides(
13581        0..4,
13582        vec![
13583            indent_guide(buffer_id, 1, 3, 0),
13584            indent_guide(buffer_id, 2, 2, 1),
13585        ],
13586        Some(vec![0]),
13587        &mut cx,
13588    );
13589}
13590
13591#[gpui::test]
13592async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13593    let (buffer_id, mut cx) = setup_indent_guides_editor(
13594        &"
13595    fn main() {
13596        let a = 1;
13597
13598        let b = 2;
13599    }"
13600        .unindent(),
13601        cx,
13602    )
13603    .await;
13604
13605    cx.update_editor(|editor, cx| {
13606        editor.change_selections(None, cx, |s| {
13607            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13608        });
13609    });
13610
13611    assert_indent_guides(
13612        0..5,
13613        vec![indent_guide(buffer_id, 1, 3, 0)],
13614        Some(vec![0]),
13615        &mut cx,
13616    );
13617}
13618
13619#[gpui::test]
13620async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13621    let (buffer_id, mut cx) = setup_indent_guides_editor(
13622        &"
13623    def m:
13624        a = 1
13625        pass"
13626            .unindent(),
13627        cx,
13628    )
13629    .await;
13630
13631    cx.update_editor(|editor, cx| {
13632        editor.change_selections(None, cx, |s| {
13633            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13634        });
13635    });
13636
13637    assert_indent_guides(
13638        0..3,
13639        vec![indent_guide(buffer_id, 1, 2, 0)],
13640        Some(vec![0]),
13641        &mut cx,
13642    );
13643}
13644
13645#[gpui::test]
13646fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13647    init_test(cx, |_| {});
13648
13649    let editor = cx.add_window(|cx| {
13650        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13651        build_editor(buffer, cx)
13652    });
13653
13654    let render_args = Arc::new(Mutex::new(None));
13655    let snapshot = editor
13656        .update(cx, |editor, cx| {
13657            let snapshot = editor.buffer().read(cx).snapshot(cx);
13658            let range =
13659                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13660
13661            struct RenderArgs {
13662                row: MultiBufferRow,
13663                folded: bool,
13664                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13665            }
13666
13667            let crease = Crease::inline(
13668                range,
13669                FoldPlaceholder::test(),
13670                {
13671                    let toggle_callback = render_args.clone();
13672                    move |row, folded, callback, _cx| {
13673                        *toggle_callback.lock() = Some(RenderArgs {
13674                            row,
13675                            folded,
13676                            callback,
13677                        });
13678                        div()
13679                    }
13680                },
13681                |_row, _folded, _cx| div(),
13682            );
13683
13684            editor.insert_creases(Some(crease), cx);
13685            let snapshot = editor.snapshot(cx);
13686            let _div =
13687                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13688            snapshot
13689        })
13690        .unwrap();
13691
13692    let render_args = render_args.lock().take().unwrap();
13693    assert_eq!(render_args.row, MultiBufferRow(1));
13694    assert!(!render_args.folded);
13695    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13696
13697    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13698        .unwrap();
13699    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13700    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13701
13702    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13703        .unwrap();
13704    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13705    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13706}
13707
13708#[gpui::test]
13709async fn test_input_text(cx: &mut gpui::TestAppContext) {
13710    init_test(cx, |_| {});
13711    let mut cx = EditorTestContext::new(cx).await;
13712
13713    cx.set_state(
13714        &r#"ˇone
13715        two
13716
13717        three
13718        fourˇ
13719        five
13720
13721        siˇx"#
13722            .unindent(),
13723    );
13724
13725    cx.dispatch_action(HandleInput(String::new()));
13726    cx.assert_editor_state(
13727        &r#"ˇone
13728        two
13729
13730        three
13731        fourˇ
13732        five
13733
13734        siˇx"#
13735            .unindent(),
13736    );
13737
13738    cx.dispatch_action(HandleInput("AAAA".to_string()));
13739    cx.assert_editor_state(
13740        &r#"AAAAˇone
13741        two
13742
13743        three
13744        fourAAAAˇ
13745        five
13746
13747        siAAAAˇx"#
13748            .unindent(),
13749    );
13750}
13751
13752#[gpui::test]
13753async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13754    init_test(cx, |_| {});
13755
13756    let mut cx = EditorTestContext::new(cx).await;
13757    cx.set_state(
13758        r#"let foo = 1;
13759let foo = 2;
13760let foo = 3;
13761let fooˇ = 4;
13762let foo = 5;
13763let foo = 6;
13764let foo = 7;
13765let foo = 8;
13766let foo = 9;
13767let foo = 10;
13768let foo = 11;
13769let foo = 12;
13770let foo = 13;
13771let foo = 14;
13772let foo = 15;"#,
13773    );
13774
13775    cx.update_editor(|e, cx| {
13776        assert_eq!(
13777            e.next_scroll_position,
13778            NextScrollCursorCenterTopBottom::Center,
13779            "Default next scroll direction is center",
13780        );
13781
13782        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13783        assert_eq!(
13784            e.next_scroll_position,
13785            NextScrollCursorCenterTopBottom::Top,
13786            "After center, next scroll direction should be top",
13787        );
13788
13789        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13790        assert_eq!(
13791            e.next_scroll_position,
13792            NextScrollCursorCenterTopBottom::Bottom,
13793            "After top, next scroll direction should be bottom",
13794        );
13795
13796        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13797        assert_eq!(
13798            e.next_scroll_position,
13799            NextScrollCursorCenterTopBottom::Center,
13800            "After bottom, scrolling should start over",
13801        );
13802
13803        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13804        assert_eq!(
13805            e.next_scroll_position,
13806            NextScrollCursorCenterTopBottom::Top,
13807            "Scrolling continues if retriggered fast enough"
13808        );
13809    });
13810
13811    cx.executor()
13812        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13813    cx.executor().run_until_parked();
13814    cx.update_editor(|e, _| {
13815        assert_eq!(
13816            e.next_scroll_position,
13817            NextScrollCursorCenterTopBottom::Center,
13818            "If scrolling is not triggered fast enough, it should reset"
13819        );
13820    });
13821}
13822
13823#[gpui::test]
13824async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13825    init_test(cx, |_| {});
13826    let mut cx = EditorLspTestContext::new_rust(
13827        lsp::ServerCapabilities {
13828            definition_provider: Some(lsp::OneOf::Left(true)),
13829            references_provider: Some(lsp::OneOf::Left(true)),
13830            ..lsp::ServerCapabilities::default()
13831        },
13832        cx,
13833    )
13834    .await;
13835
13836    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13837        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13838            move |params, _| async move {
13839                if empty_go_to_definition {
13840                    Ok(None)
13841                } else {
13842                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13843                        uri: params.text_document_position_params.text_document.uri,
13844                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13845                    })))
13846                }
13847            },
13848        );
13849        let references =
13850            cx.lsp
13851                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13852                    Ok(Some(vec![lsp::Location {
13853                        uri: params.text_document_position.text_document.uri,
13854                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13855                    }]))
13856                });
13857        (go_to_definition, references)
13858    };
13859
13860    cx.set_state(
13861        &r#"fn one() {
13862            let mut a = ˇtwo();
13863        }
13864
13865        fn two() {}"#
13866            .unindent(),
13867    );
13868    set_up_lsp_handlers(false, &mut cx);
13869    let navigated = cx
13870        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13871        .await
13872        .expect("Failed to navigate to definition");
13873    assert_eq!(
13874        navigated,
13875        Navigated::Yes,
13876        "Should have navigated to definition from the GetDefinition response"
13877    );
13878    cx.assert_editor_state(
13879        &r#"fn one() {
13880            let mut a = two();
13881        }
13882
13883        fn «twoˇ»() {}"#
13884            .unindent(),
13885    );
13886
13887    let editors = cx.update_workspace(|workspace, cx| {
13888        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13889    });
13890    cx.update_editor(|_, test_editor_cx| {
13891        assert_eq!(
13892            editors.len(),
13893            1,
13894            "Initially, only one, test, editor should be open in the workspace"
13895        );
13896        assert_eq!(
13897            test_editor_cx.view(),
13898            editors.last().expect("Asserted len is 1")
13899        );
13900    });
13901
13902    set_up_lsp_handlers(true, &mut cx);
13903    let navigated = cx
13904        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13905        .await
13906        .expect("Failed to navigate to lookup references");
13907    assert_eq!(
13908        navigated,
13909        Navigated::Yes,
13910        "Should have navigated to references as a fallback after empty GoToDefinition response"
13911    );
13912    // We should not change the selections in the existing file,
13913    // if opening another milti buffer with the references
13914    cx.assert_editor_state(
13915        &r#"fn one() {
13916            let mut a = two();
13917        }
13918
13919        fn «twoˇ»() {}"#
13920            .unindent(),
13921    );
13922    let editors = cx.update_workspace(|workspace, cx| {
13923        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13924    });
13925    cx.update_editor(|_, test_editor_cx| {
13926        assert_eq!(
13927            editors.len(),
13928            2,
13929            "After falling back to references search, we open a new editor with the results"
13930        );
13931        let references_fallback_text = editors
13932            .into_iter()
13933            .find(|new_editor| new_editor != test_editor_cx.view())
13934            .expect("Should have one non-test editor now")
13935            .read(test_editor_cx)
13936            .text(test_editor_cx);
13937        assert_eq!(
13938            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13939            "Should use the range from the references response and not the GoToDefinition one"
13940        );
13941    });
13942}
13943
13944#[gpui::test]
13945async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13946    init_test(cx, |_| {});
13947
13948    let language = Arc::new(Language::new(
13949        LanguageConfig::default(),
13950        Some(tree_sitter_rust::LANGUAGE.into()),
13951    ));
13952
13953    let text = r#"
13954        #[cfg(test)]
13955        mod tests() {
13956            #[test]
13957            fn runnable_1() {
13958                let a = 1;
13959            }
13960
13961            #[test]
13962            fn runnable_2() {
13963                let a = 1;
13964                let b = 2;
13965            }
13966        }
13967    "#
13968    .unindent();
13969
13970    let fs = FakeFs::new(cx.executor());
13971    fs.insert_file("/file.rs", Default::default()).await;
13972
13973    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13974    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13975    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13976    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13977    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13978
13979    let editor = cx.new_view(|cx| {
13980        Editor::new(
13981            EditorMode::Full,
13982            multi_buffer,
13983            Some(project.clone()),
13984            true,
13985            cx,
13986        )
13987    });
13988
13989    editor.update(cx, |editor, cx| {
13990        editor.tasks.insert(
13991            (buffer.read(cx).remote_id(), 3),
13992            RunnableTasks {
13993                templates: vec![],
13994                offset: MultiBufferOffset(43),
13995                column: 0,
13996                extra_variables: HashMap::default(),
13997                context_range: BufferOffset(43)..BufferOffset(85),
13998            },
13999        );
14000        editor.tasks.insert(
14001            (buffer.read(cx).remote_id(), 8),
14002            RunnableTasks {
14003                templates: vec![],
14004                offset: MultiBufferOffset(86),
14005                column: 0,
14006                extra_variables: HashMap::default(),
14007                context_range: BufferOffset(86)..BufferOffset(191),
14008            },
14009        );
14010
14011        // Test finding task when cursor is inside function body
14012        editor.change_selections(None, cx, |s| {
14013            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14014        });
14015        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14016        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14017
14018        // Test finding task when cursor is on function name
14019        editor.change_selections(None, cx, |s| {
14020            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14021        });
14022        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14023        assert_eq!(row, 8, "Should find task when cursor is on function name");
14024    });
14025}
14026
14027fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
14028    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
14029    point..point
14030}
14031
14032fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
14033    let (text, ranges) = marked_text_ranges(marked_text, true);
14034    assert_eq!(view.text(cx), text);
14035    assert_eq!(
14036        view.selections.ranges(cx),
14037        ranges,
14038        "Assert selections are {}",
14039        marked_text
14040    );
14041}
14042
14043pub fn handle_signature_help_request(
14044    cx: &mut EditorLspTestContext,
14045    mocked_response: lsp::SignatureHelp,
14046) -> impl Future<Output = ()> {
14047    let mut request =
14048        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
14049            let mocked_response = mocked_response.clone();
14050            async move { Ok(Some(mocked_response)) }
14051        });
14052
14053    async move {
14054        request.next().await;
14055    }
14056}
14057
14058/// Handle completion request passing a marked string specifying where the completion
14059/// should be triggered from using '|' character, what range should be replaced, and what completions
14060/// should be returned using '<' and '>' to delimit the range
14061pub fn handle_completion_request(
14062    cx: &mut EditorLspTestContext,
14063    marked_string: &str,
14064    completions: Vec<&'static str>,
14065    counter: Arc<AtomicUsize>,
14066) -> impl Future<Output = ()> {
14067    let complete_from_marker: TextRangeMarker = '|'.into();
14068    let replace_range_marker: TextRangeMarker = ('<', '>').into();
14069    let (_, mut marked_ranges) = marked_text_ranges_by(
14070        marked_string,
14071        vec![complete_from_marker.clone(), replace_range_marker.clone()],
14072    );
14073
14074    let complete_from_position =
14075        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
14076    let replace_range =
14077        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
14078
14079    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
14080        let completions = completions.clone();
14081        counter.fetch_add(1, atomic::Ordering::Release);
14082        async move {
14083            assert_eq!(params.text_document_position.text_document.uri, url.clone());
14084            assert_eq!(
14085                params.text_document_position.position,
14086                complete_from_position
14087            );
14088            Ok(Some(lsp::CompletionResponse::Array(
14089                completions
14090                    .iter()
14091                    .map(|completion_text| lsp::CompletionItem {
14092                        label: completion_text.to_string(),
14093                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14094                            range: replace_range,
14095                            new_text: completion_text.to_string(),
14096                        })),
14097                        ..Default::default()
14098                    })
14099                    .collect(),
14100            )))
14101        }
14102    });
14103
14104    async move {
14105        request.next().await;
14106    }
14107}
14108
14109fn handle_resolve_completion_request(
14110    cx: &mut EditorLspTestContext,
14111    edits: Option<Vec<(&'static str, &'static str)>>,
14112) -> impl Future<Output = ()> {
14113    let edits = edits.map(|edits| {
14114        edits
14115            .iter()
14116            .map(|(marked_string, new_text)| {
14117                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
14118                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
14119                lsp::TextEdit::new(replace_range, new_text.to_string())
14120            })
14121            .collect::<Vec<_>>()
14122    });
14123
14124    let mut request =
14125        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14126            let edits = edits.clone();
14127            async move {
14128                Ok(lsp::CompletionItem {
14129                    additional_text_edits: edits,
14130                    ..Default::default()
14131                })
14132            }
14133        });
14134
14135    async move {
14136        request.next().await;
14137    }
14138}
14139
14140pub(crate) fn update_test_language_settings(
14141    cx: &mut TestAppContext,
14142    f: impl Fn(&mut AllLanguageSettingsContent),
14143) {
14144    cx.update(|cx| {
14145        SettingsStore::update_global(cx, |store, cx| {
14146            store.update_user_settings::<AllLanguageSettings>(cx, f);
14147        });
14148    });
14149}
14150
14151pub(crate) fn update_test_project_settings(
14152    cx: &mut TestAppContext,
14153    f: impl Fn(&mut ProjectSettings),
14154) {
14155    cx.update(|cx| {
14156        SettingsStore::update_global(cx, |store, cx| {
14157            store.update_user_settings::<ProjectSettings>(cx, f);
14158        });
14159    });
14160}
14161
14162pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14163    cx.update(|cx| {
14164        assets::Assets.load_test_fonts(cx);
14165        let store = SettingsStore::test(cx);
14166        cx.set_global(store);
14167        theme::init(theme::LoadThemes::JustBase, cx);
14168        release_channel::init(SemanticVersion::default(), cx);
14169        client::init_settings(cx);
14170        language::init(cx);
14171        Project::init_settings(cx);
14172        workspace::init_settings(cx);
14173        crate::init(cx);
14174    });
14175
14176    update_test_language_settings(cx, f);
14177}
14178
14179#[track_caller]
14180fn assert_hunk_revert(
14181    not_reverted_text_with_selections: &str,
14182    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14183    expected_reverted_text_with_selections: &str,
14184    base_text: &str,
14185    cx: &mut EditorLspTestContext,
14186) {
14187    cx.set_state(not_reverted_text_with_selections);
14188    cx.update_editor(|editor, cx| {
14189        editor
14190            .buffer()
14191            .read(cx)
14192            .as_singleton()
14193            .unwrap()
14194            .update(cx, |buffer, cx| {
14195                buffer.set_diff_base(Some(base_text.into()), cx);
14196            });
14197    });
14198    cx.executor().run_until_parked();
14199
14200    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14201        let snapshot = editor.buffer().read(cx).snapshot(cx);
14202        let reverted_hunk_statuses = snapshot
14203            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
14204            .map(|hunk| hunk_status(&hunk))
14205            .collect::<Vec<_>>();
14206
14207        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14208        reverted_hunk_statuses
14209    });
14210    cx.executor().run_until_parked();
14211    cx.assert_editor_state(expected_reverted_text_with_selections);
14212    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14213}