editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45};
   46use workspace::{
   47    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   48    NavigationEntry, ViewId,
   49};
   50
   51#[gpui::test]
   52fn test_edit_events(cx: &mut TestAppContext) {
   53    init_test(cx, |_| {});
   54
   55    let buffer = cx.new_model(|cx| {
   56        let mut buffer = language::Buffer::local("123456", cx);
   57        buffer.set_group_interval(Duration::from_secs(1));
   58        buffer
   59    });
   60
   61    let events = Rc::new(RefCell::new(Vec::new()));
   62    let editor1 = cx.add_window({
   63        let events = events.clone();
   64        |cx| {
   65            let view = cx.view().clone();
   66            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   67                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   68                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   69                _ => {}
   70            })
   71            .detach();
   72            Editor::for_buffer(buffer.clone(), None, cx)
   73        }
   74    });
   75
   76    let editor2 = cx.add_window({
   77        let events = events.clone();
   78        |cx| {
   79            cx.subscribe(
   80                &cx.view().clone(),
   81                move |_, _, event: &EditorEvent, _| match event {
   82                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   83                    EditorEvent::BufferEdited => {
   84                        events.borrow_mut().push(("editor2", "buffer edited"))
   85                    }
   86                    _ => {}
   87                },
   88            )
   89            .detach();
   90            Editor::for_buffer(buffer.clone(), None, cx)
   91        }
   92    });
   93
   94    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   95
   96    // Mutating editor 1 will emit an `Edited` event only for that editor.
   97    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   98    assert_eq!(
   99        mem::take(&mut *events.borrow_mut()),
  100        [
  101            ("editor1", "edited"),
  102            ("editor1", "buffer edited"),
  103            ("editor2", "buffer edited"),
  104        ]
  105    );
  106
  107    // Mutating editor 2 will emit an `Edited` event only for that editor.
  108    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  109    assert_eq!(
  110        mem::take(&mut *events.borrow_mut()),
  111        [
  112            ("editor2", "edited"),
  113            ("editor1", "buffer edited"),
  114            ("editor2", "buffer edited"),
  115        ]
  116    );
  117
  118    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  119    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  120    assert_eq!(
  121        mem::take(&mut *events.borrow_mut()),
  122        [
  123            ("editor1", "edited"),
  124            ("editor1", "buffer edited"),
  125            ("editor2", "buffer edited"),
  126        ]
  127    );
  128
  129    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  130    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  131    assert_eq!(
  132        mem::take(&mut *events.borrow_mut()),
  133        [
  134            ("editor1", "edited"),
  135            ("editor1", "buffer edited"),
  136            ("editor2", "buffer edited"),
  137        ]
  138    );
  139
  140    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  141    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  142    assert_eq!(
  143        mem::take(&mut *events.borrow_mut()),
  144        [
  145            ("editor2", "edited"),
  146            ("editor1", "buffer edited"),
  147            ("editor2", "buffer edited"),
  148        ]
  149    );
  150
  151    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  152    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  153    assert_eq!(
  154        mem::take(&mut *events.borrow_mut()),
  155        [
  156            ("editor2", "edited"),
  157            ("editor1", "buffer edited"),
  158            ("editor2", "buffer edited"),
  159        ]
  160    );
  161
  162    // No event is emitted when the mutation is a no-op.
  163    _ = editor2.update(cx, |editor, cx| {
  164        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  165
  166        editor.backspace(&Backspace, cx);
  167    });
  168    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  169}
  170
  171#[gpui::test]
  172fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  173    init_test(cx, |_| {});
  174
  175    let mut now = Instant::now();
  176    let group_interval = Duration::from_millis(1);
  177    let buffer = cx.new_model(|cx| {
  178        let mut buf = language::Buffer::local("123456", cx);
  179        buf.set_group_interval(group_interval);
  180        buf
  181    });
  182    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  183    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  184
  185    _ = editor.update(cx, |editor, cx| {
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  188
  189        editor.insert("cd", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cd56");
  192        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  193
  194        editor.start_transaction_at(now, cx);
  195        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  196        editor.insert("e", cx);
  197        editor.end_transaction_at(now, cx);
  198        assert_eq!(editor.text(cx), "12cde6");
  199        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  200
  201        now += group_interval + Duration::from_millis(1);
  202        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  203
  204        // Simulate an edit in another editor
  205        buffer.update(cx, |buffer, cx| {
  206            buffer.start_transaction_at(now, cx);
  207            buffer.edit([(0..1, "a")], None, cx);
  208            buffer.edit([(1..1, "b")], None, cx);
  209            buffer.end_transaction_at(now, cx);
  210        });
  211
  212        assert_eq!(editor.text(cx), "ab2cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  214
  215        // Last transaction happened past the group interval in a different editor.
  216        // Undo it individually and don't restore selections.
  217        editor.undo(&Undo, cx);
  218        assert_eq!(editor.text(cx), "12cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  220
  221        // First two transactions happened within the group interval in this editor.
  222        // Undo them together and restore selections.
  223        editor.undo(&Undo, cx);
  224        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  225        assert_eq!(editor.text(cx), "123456");
  226        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  227
  228        // Redo the first two transactions together.
  229        editor.redo(&Redo, cx);
  230        assert_eq!(editor.text(cx), "12cde6");
  231        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  232
  233        // Redo the last transaction on its own.
  234        editor.redo(&Redo, cx);
  235        assert_eq!(editor.text(cx), "ab2cde6");
  236        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  237
  238        // Test empty transactions.
  239        editor.start_transaction_at(now, cx);
  240        editor.end_transaction_at(now, cx);
  241        editor.undo(&Undo, cx);
  242        assert_eq!(editor.text(cx), "12cde6");
  243    });
  244}
  245
  246#[gpui::test]
  247fn test_ime_composition(cx: &mut TestAppContext) {
  248    init_test(cx, |_| {});
  249
  250    let buffer = cx.new_model(|cx| {
  251        let mut buffer = language::Buffer::local("abcde", cx);
  252        // Ensure automatic grouping doesn't occur.
  253        buffer.set_group_interval(Duration::ZERO);
  254        buffer
  255    });
  256
  257    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  258    cx.add_window(|cx| {
  259        let mut editor = build_editor(buffer.clone(), cx);
  260
  261        // Start a new IME composition.
  262        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  263        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  264        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  265        assert_eq!(editor.text(cx), "äbcde");
  266        assert_eq!(
  267            editor.marked_text_ranges(cx),
  268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  269        );
  270
  271        // Finalize IME composition.
  272        editor.replace_text_in_range(None, "ā", cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // IME composition edits are grouped and are undone/redone at once.
  277        editor.undo(&Default::default(), cx);
  278        assert_eq!(editor.text(cx), "abcde");
  279        assert_eq!(editor.marked_text_ranges(cx), None);
  280        editor.redo(&Default::default(), cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // Start a new IME composition.
  285        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  286        assert_eq!(
  287            editor.marked_text_ranges(cx),
  288            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  289        );
  290
  291        // Undoing during an IME composition cancels it.
  292        editor.undo(&Default::default(), cx);
  293        assert_eq!(editor.text(cx), "ābcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295
  296        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  297        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  298        assert_eq!(editor.text(cx), "ābcdè");
  299        assert_eq!(
  300            editor.marked_text_ranges(cx),
  301            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  302        );
  303
  304        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  305        editor.replace_text_in_range(Some(4..999), "ę", cx);
  306        assert_eq!(editor.text(cx), "ābcdę");
  307        assert_eq!(editor.marked_text_ranges(cx), None);
  308
  309        // Start a new IME composition with multiple cursors.
  310        editor.change_selections(None, cx, |s| {
  311            s.select_ranges([
  312                OffsetUtf16(1)..OffsetUtf16(1),
  313                OffsetUtf16(3)..OffsetUtf16(3),
  314                OffsetUtf16(5)..OffsetUtf16(5),
  315            ])
  316        });
  317        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  318        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  319        assert_eq!(
  320            editor.marked_text_ranges(cx),
  321            Some(vec![
  322                OffsetUtf16(0)..OffsetUtf16(3),
  323                OffsetUtf16(4)..OffsetUtf16(7),
  324                OffsetUtf16(8)..OffsetUtf16(11)
  325            ])
  326        );
  327
  328        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  329        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  330        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  331        assert_eq!(
  332            editor.marked_text_ranges(cx),
  333            Some(vec![
  334                OffsetUtf16(1)..OffsetUtf16(2),
  335                OffsetUtf16(5)..OffsetUtf16(6),
  336                OffsetUtf16(9)..OffsetUtf16(10)
  337            ])
  338        );
  339
  340        // Finalize IME composition with multiple cursors.
  341        editor.replace_text_in_range(Some(9..10), "2", cx);
  342        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  343        assert_eq!(editor.marked_text_ranges(cx), None);
  344
  345        editor
  346    });
  347}
  348
  349#[gpui::test]
  350fn test_selection_with_mouse(cx: &mut TestAppContext) {
  351    init_test(cx, |_| {});
  352
  353    let editor = cx.add_window(|cx| {
  354        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  355        build_editor(buffer, cx)
  356    });
  357
  358    _ = editor.update(cx, |view, cx| {
  359        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  360    });
  361    assert_eq!(
  362        editor
  363            .update(cx, |view, cx| view.selections.display_ranges(cx))
  364            .unwrap(),
  365        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  366    );
  367
  368    _ = editor.update(cx, |view, cx| {
  369        view.update_selection(
  370            DisplayPoint::new(DisplayRow(3), 3),
  371            0,
  372            gpui::Point::<f32>::default(),
  373            cx,
  374        );
  375    });
  376
  377    assert_eq!(
  378        editor
  379            .update(cx, |view, cx| view.selections.display_ranges(cx))
  380            .unwrap(),
  381        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  382    );
  383
  384    _ = editor.update(cx, |view, cx| {
  385        view.update_selection(
  386            DisplayPoint::new(DisplayRow(1), 1),
  387            0,
  388            gpui::Point::<f32>::default(),
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |view, cx| view.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  398    );
  399
  400    _ = editor.update(cx, |view, cx| {
  401        view.end_selection(cx);
  402        view.update_selection(
  403            DisplayPoint::new(DisplayRow(3), 3),
  404            0,
  405            gpui::Point::<f32>::default(),
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |view, cx| view.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |view, cx| {
  418        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  419        view.update_selection(
  420            DisplayPoint::new(DisplayRow(0), 0),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |view, cx| view.selections.display_ranges(cx))
  430            .unwrap(),
  431        [
  432            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  433            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  434        ]
  435    );
  436
  437    _ = editor.update(cx, |view, cx| {
  438        view.end_selection(cx);
  439    });
  440
  441    assert_eq!(
  442        editor
  443            .update(cx, |view, cx| view.selections.display_ranges(cx))
  444            .unwrap(),
  445        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  446    );
  447}
  448
  449#[gpui::test]
  450fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  451    init_test(cx, |_| {});
  452
  453    let editor = cx.add_window(|cx| {
  454        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  455        build_editor(buffer, cx)
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  468    });
  469
  470    _ = editor.update(cx, |view, cx| {
  471        view.end_selection(cx);
  472    });
  473
  474    assert_eq!(
  475        editor
  476            .update(cx, |view, cx| view.selections.display_ranges(cx))
  477            .unwrap(),
  478        [
  479            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  480            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  481        ]
  482    );
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  486    });
  487
  488    _ = editor.update(cx, |view, cx| {
  489        view.end_selection(cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |view, cx| view.selections.display_ranges(cx))
  495            .unwrap(),
  496        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  497    );
  498}
  499
  500#[gpui::test]
  501fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  502    init_test(cx, |_| {});
  503
  504    let view = cx.add_window(|cx| {
  505        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  506        build_editor(buffer, cx)
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  511        assert_eq!(
  512            view.selections.display_ranges(cx),
  513            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  514        );
  515    });
  516
  517    _ = view.update(cx, |view, cx| {
  518        view.update_selection(
  519            DisplayPoint::new(DisplayRow(3), 3),
  520            0,
  521            gpui::Point::<f32>::default(),
  522            cx,
  523        );
  524        assert_eq!(
  525            view.selections.display_ranges(cx),
  526            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  527        );
  528    });
  529
  530    _ = view.update(cx, |view, cx| {
  531        view.cancel(&Cancel, cx);
  532        view.update_selection(
  533            DisplayPoint::new(DisplayRow(1), 1),
  534            0,
  535            gpui::Point::<f32>::default(),
  536            cx,
  537        );
  538        assert_eq!(
  539            view.selections.display_ranges(cx),
  540            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  541        );
  542    });
  543}
  544
  545#[gpui::test]
  546fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  547    init_test(cx, |_| {});
  548
  549    let view = cx.add_window(|cx| {
  550        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  551        build_editor(buffer, cx)
  552    });
  553
  554    _ = view.update(cx, |view, cx| {
  555        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  556        assert_eq!(
  557            view.selections.display_ranges(cx),
  558            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  559        );
  560
  561        view.move_down(&Default::default(), cx);
  562        assert_eq!(
  563            view.selections.display_ranges(cx),
  564            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  565        );
  566
  567        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  568        assert_eq!(
  569            view.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        view.move_up(&Default::default(), cx);
  574        assert_eq!(
  575            view.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  577        );
  578    });
  579}
  580
  581#[gpui::test]
  582fn test_clone(cx: &mut TestAppContext) {
  583    init_test(cx, |_| {});
  584
  585    let (text, selection_ranges) = marked_text_ranges(
  586        indoc! {"
  587            one
  588            two
  589            threeˇ
  590            four
  591            fiveˇ
  592        "},
  593        true,
  594    );
  595
  596    let editor = cx.add_window(|cx| {
  597        let buffer = MultiBuffer::build_simple(&text, cx);
  598        build_editor(buffer, cx)
  599    });
  600
  601    _ = editor.update(cx, |editor, cx| {
  602        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  603        editor.fold_creases(
  604            vec![
  605                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  606                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  607            ],
  608            true,
  609            cx,
  610        );
  611    });
  612
  613    let cloned_editor = editor
  614        .update(cx, |editor, cx| {
  615            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  616        })
  617        .unwrap()
  618        .unwrap();
  619
  620    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  621    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  622
  623    assert_eq!(
  624        cloned_editor
  625            .update(cx, |e, cx| e.display_text(cx))
  626            .unwrap(),
  627        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  628    );
  629    assert_eq!(
  630        cloned_snapshot
  631            .folds_in_range(0..text.len())
  632            .collect::<Vec<_>>(),
  633        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |editor, cx| editor.selections.ranges(cx))
  641            .unwrap()
  642    );
  643    assert_set_eq!(
  644        cloned_editor
  645            .update(cx, |e, cx| e.selections.display_ranges(cx))
  646            .unwrap(),
  647        editor
  648            .update(cx, |e, cx| e.selections.display_ranges(cx))
  649            .unwrap()
  650    );
  651}
  652
  653#[gpui::test]
  654async fn test_navigation_history(cx: &mut TestAppContext) {
  655    init_test(cx, |_| {});
  656
  657    use workspace::item::Item;
  658
  659    let fs = FakeFs::new(cx.executor());
  660    let project = Project::test(fs, [], cx).await;
  661    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  662    let pane = workspace
  663        .update(cx, |workspace, _| workspace.active_pane().clone())
  664        .unwrap();
  665
  666    _ = workspace.update(cx, |_v, cx| {
  667        cx.new_view(|cx| {
  668            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  669            let mut editor = build_editor(buffer.clone(), cx);
  670            let handle = cx.view();
  671            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  672
  673            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  674                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  675            }
  676
  677            // Move the cursor a small distance.
  678            // Nothing is added to the navigation history.
  679            editor.change_selections(None, cx, |s| {
  680                s.select_display_ranges([
  681                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  682                ])
  683            });
  684            editor.change_selections(None, cx, |s| {
  685                s.select_display_ranges([
  686                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  687                ])
  688            });
  689            assert!(pop_history(&mut editor, cx).is_none());
  690
  691            // Move the cursor a large distance.
  692            // The history can jump back to the previous position.
  693            editor.change_selections(None, cx, |s| {
  694                s.select_display_ranges([
  695                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  696                ])
  697            });
  698            let nav_entry = pop_history(&mut editor, cx).unwrap();
  699            editor.navigate(nav_entry.data.unwrap(), cx);
  700            assert_eq!(nav_entry.item.id(), cx.entity_id());
  701            assert_eq!(
  702                editor.selections.display_ranges(cx),
  703                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  704            );
  705            assert!(pop_history(&mut editor, cx).is_none());
  706
  707            // Move the cursor a small distance via the mouse.
  708            // Nothing is added to the navigation history.
  709            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  710            editor.end_selection(cx);
  711            assert_eq!(
  712                editor.selections.display_ranges(cx),
  713                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  714            );
  715            assert!(pop_history(&mut editor, cx).is_none());
  716
  717            // Move the cursor a large distance via the mouse.
  718            // The history can jump back to the previous position.
  719            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  720            editor.end_selection(cx);
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  724            );
  725            let nav_entry = pop_history(&mut editor, cx).unwrap();
  726            editor.navigate(nav_entry.data.unwrap(), cx);
  727            assert_eq!(nav_entry.item.id(), cx.entity_id());
  728            assert_eq!(
  729                editor.selections.display_ranges(cx),
  730                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  731            );
  732            assert!(pop_history(&mut editor, cx).is_none());
  733
  734            // Set scroll position to check later
  735            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  736            let original_scroll_position = editor.scroll_manager.anchor();
  737
  738            // Jump to the end of the document and adjust scroll
  739            editor.move_to_end(&MoveToEnd, cx);
  740            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  741            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  742
  743            let nav_entry = pop_history(&mut editor, cx).unwrap();
  744            editor.navigate(nav_entry.data.unwrap(), cx);
  745            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  746
  747            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  748            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  749            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  750            let invalid_point = Point::new(9999, 0);
  751            editor.navigate(
  752                Box::new(NavigationData {
  753                    cursor_anchor: invalid_anchor,
  754                    cursor_position: invalid_point,
  755                    scroll_anchor: ScrollAnchor {
  756                        anchor: invalid_anchor,
  757                        offset: Default::default(),
  758                    },
  759                    scroll_top_row: invalid_point.row,
  760                }),
  761                cx,
  762            );
  763            assert_eq!(
  764                editor.selections.display_ranges(cx),
  765                &[editor.max_point(cx)..editor.max_point(cx)]
  766            );
  767            assert_eq!(
  768                editor.scroll_position(cx),
  769                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  770            );
  771
  772            editor
  773        })
  774    });
  775}
  776
  777#[gpui::test]
  778fn test_cancel(cx: &mut TestAppContext) {
  779    init_test(cx, |_| {});
  780
  781    let view = cx.add_window(|cx| {
  782        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  783        build_editor(buffer, cx)
  784    });
  785
  786    _ = view.update(cx, |view, cx| {
  787        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  788        view.update_selection(
  789            DisplayPoint::new(DisplayRow(1), 1),
  790            0,
  791            gpui::Point::<f32>::default(),
  792            cx,
  793        );
  794        view.end_selection(cx);
  795
  796        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  797        view.update_selection(
  798            DisplayPoint::new(DisplayRow(0), 3),
  799            0,
  800            gpui::Point::<f32>::default(),
  801            cx,
  802        );
  803        view.end_selection(cx);
  804        assert_eq!(
  805            view.selections.display_ranges(cx),
  806            [
  807                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  808                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  809            ]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820
  821    _ = view.update(cx, |view, cx| {
  822        view.cancel(&Cancel, cx);
  823        assert_eq!(
  824            view.selections.display_ranges(cx),
  825            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  826        );
  827    });
  828}
  829
  830#[gpui::test]
  831fn test_fold_action(cx: &mut TestAppContext) {
  832    init_test(cx, |_| {});
  833
  834    let view = cx.add_window(|cx| {
  835        let buffer = MultiBuffer::build_simple(
  836            &"
  837                impl Foo {
  838                    // Hello!
  839
  840                    fn a() {
  841                        1
  842                    }
  843
  844                    fn b() {
  845                        2
  846                    }
  847
  848                    fn c() {
  849                        3
  850                    }
  851                }
  852            "
  853            .unindent(),
  854            cx,
  855        );
  856        build_editor(buffer.clone(), cx)
  857    });
  858
  859    _ = view.update(cx, |view, cx| {
  860        view.change_selections(None, cx, |s| {
  861            s.select_display_ranges([
  862                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  863            ]);
  864        });
  865        view.fold(&Fold, cx);
  866        assert_eq!(
  867            view.display_text(cx),
  868            "
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {⋯
  877                    }
  878
  879                    fn c() {⋯
  880                    }
  881                }
  882            "
  883            .unindent(),
  884        );
  885
  886        view.fold(&Fold, cx);
  887        assert_eq!(
  888            view.display_text(cx),
  889            "
  890                impl Foo {⋯
  891                }
  892            "
  893            .unindent(),
  894        );
  895
  896        view.unfold_lines(&UnfoldLines, cx);
  897        assert_eq!(
  898            view.display_text(cx),
  899            "
  900                impl Foo {
  901                    // Hello!
  902
  903                    fn a() {
  904                        1
  905                    }
  906
  907                    fn b() {⋯
  908                    }
  909
  910                    fn c() {⋯
  911                    }
  912                }
  913            "
  914            .unindent(),
  915        );
  916
  917        view.unfold_lines(&UnfoldLines, cx);
  918        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  919    });
  920}
  921
  922#[gpui::test]
  923fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  924    init_test(cx, |_| {});
  925
  926    let view = cx.add_window(|cx| {
  927        let buffer = MultiBuffer::build_simple(
  928            &"
  929                class Foo:
  930                    # Hello!
  931
  932                    def a():
  933                        print(1)
  934
  935                    def b():
  936                        print(2)
  937
  938                    def c():
  939                        print(3)
  940            "
  941            .unindent(),
  942            cx,
  943        );
  944        build_editor(buffer.clone(), cx)
  945    });
  946
  947    _ = view.update(cx, |view, cx| {
  948        view.change_selections(None, cx, |s| {
  949            s.select_display_ranges([
  950                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  951            ]);
  952        });
  953        view.fold(&Fold, cx);
  954        assert_eq!(
  955            view.display_text(cx),
  956            "
  957                class Foo:
  958                    # Hello!
  959
  960                    def a():
  961                        print(1)
  962
  963                    def b():⋯
  964
  965                    def c():⋯
  966            "
  967            .unindent(),
  968        );
  969
  970        view.fold(&Fold, cx);
  971        assert_eq!(
  972            view.display_text(cx),
  973            "
  974                class Foo:⋯
  975            "
  976            .unindent(),
  977        );
  978
  979        view.unfold_lines(&UnfoldLines, cx);
  980        assert_eq!(
  981            view.display_text(cx),
  982            "
  983                class Foo:
  984                    # Hello!
  985
  986                    def a():
  987                        print(1)
  988
  989                    def b():⋯
  990
  991                    def c():⋯
  992            "
  993            .unindent(),
  994        );
  995
  996        view.unfold_lines(&UnfoldLines, cx);
  997        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  998    });
  999}
 1000
 1001#[gpui::test]
 1002fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1003    init_test(cx, |_| {});
 1004
 1005    let view = cx.add_window(|cx| {
 1006        let buffer = MultiBuffer::build_simple(
 1007            &"
 1008                class Foo:
 1009                    # Hello!
 1010
 1011                    def a():
 1012                        print(1)
 1013
 1014                    def b():
 1015                        print(2)
 1016
 1017
 1018                    def c():
 1019                        print(3)
 1020
 1021
 1022            "
 1023            .unindent(),
 1024            cx,
 1025        );
 1026        build_editor(buffer.clone(), cx)
 1027    });
 1028
 1029    _ = view.update(cx, |view, cx| {
 1030        view.change_selections(None, cx, |s| {
 1031            s.select_display_ranges([
 1032                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1033            ]);
 1034        });
 1035        view.fold(&Fold, cx);
 1036        assert_eq!(
 1037            view.display_text(cx),
 1038            "
 1039                class Foo:
 1040                    # Hello!
 1041
 1042                    def a():
 1043                        print(1)
 1044
 1045                    def b():⋯
 1046
 1047
 1048                    def c():⋯
 1049
 1050
 1051            "
 1052            .unindent(),
 1053        );
 1054
 1055        view.fold(&Fold, cx);
 1056        assert_eq!(
 1057            view.display_text(cx),
 1058            "
 1059                class Foo:⋯
 1060
 1061
 1062            "
 1063            .unindent(),
 1064        );
 1065
 1066        view.unfold_lines(&UnfoldLines, cx);
 1067        assert_eq!(
 1068            view.display_text(cx),
 1069            "
 1070                class Foo:
 1071                    # Hello!
 1072
 1073                    def a():
 1074                        print(1)
 1075
 1076                    def b():⋯
 1077
 1078
 1079                    def c():⋯
 1080
 1081
 1082            "
 1083            .unindent(),
 1084        );
 1085
 1086        view.unfold_lines(&UnfoldLines, cx);
 1087        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1088    });
 1089}
 1090
 1091#[gpui::test]
 1092fn test_fold_at_level(cx: &mut TestAppContext) {
 1093    init_test(cx, |_| {});
 1094
 1095    let view = cx.add_window(|cx| {
 1096        let buffer = MultiBuffer::build_simple(
 1097            &"
 1098                class Foo:
 1099                    # Hello!
 1100
 1101                    def a():
 1102                        print(1)
 1103
 1104                    def b():
 1105                        print(2)
 1106
 1107
 1108                class Bar:
 1109                    # World!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():
 1115                        print(2)
 1116
 1117
 1118            "
 1119            .unindent(),
 1120            cx,
 1121        );
 1122        build_editor(buffer.clone(), cx)
 1123    });
 1124
 1125    _ = view.update(cx, |view, cx| {
 1126        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1127        assert_eq!(
 1128            view.display_text(cx),
 1129            "
 1130                class Foo:
 1131                    # Hello!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138                class Bar:
 1139                    # World!
 1140
 1141                    def a():⋯
 1142
 1143                    def b():⋯
 1144
 1145
 1146            "
 1147            .unindent(),
 1148        );
 1149
 1150        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1151        assert_eq!(
 1152            view.display_text(cx),
 1153            "
 1154                class Foo:⋯
 1155
 1156
 1157                class Bar:⋯
 1158
 1159
 1160            "
 1161            .unindent(),
 1162        );
 1163
 1164        view.unfold_all(&UnfoldAll, cx);
 1165        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1166        assert_eq!(
 1167            view.display_text(cx),
 1168            "
 1169                class Foo:
 1170                    # Hello!
 1171
 1172                    def a():
 1173                        print(1)
 1174
 1175                    def b():
 1176                        print(2)
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():
 1183                        print(1)
 1184
 1185                    def b():
 1186                        print(2)
 1187
 1188
 1189            "
 1190            .unindent(),
 1191        );
 1192
 1193        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1194    });
 1195}
 1196
 1197#[gpui::test]
 1198fn test_move_cursor(cx: &mut TestAppContext) {
 1199    init_test(cx, |_| {});
 1200
 1201    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1202    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1203
 1204    buffer.update(cx, |buffer, cx| {
 1205        buffer.edit(
 1206            vec![
 1207                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1208                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1209            ],
 1210            None,
 1211            cx,
 1212        );
 1213    });
 1214    _ = view.update(cx, |view, cx| {
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1218        );
 1219
 1220        view.move_down(&MoveDown, cx);
 1221        assert_eq!(
 1222            view.selections.display_ranges(cx),
 1223            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1224        );
 1225
 1226        view.move_right(&MoveRight, cx);
 1227        assert_eq!(
 1228            view.selections.display_ranges(cx),
 1229            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1230        );
 1231
 1232        view.move_left(&MoveLeft, cx);
 1233        assert_eq!(
 1234            view.selections.display_ranges(cx),
 1235            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1236        );
 1237
 1238        view.move_up(&MoveUp, cx);
 1239        assert_eq!(
 1240            view.selections.display_ranges(cx),
 1241            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1242        );
 1243
 1244        view.move_to_end(&MoveToEnd, cx);
 1245        assert_eq!(
 1246            view.selections.display_ranges(cx),
 1247            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1248        );
 1249
 1250        view.move_to_beginning(&MoveToBeginning, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        view.change_selections(None, cx, |s| {
 1257            s.select_display_ranges([
 1258                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1259            ]);
 1260        });
 1261        view.select_to_beginning(&SelectToBeginning, cx);
 1262        assert_eq!(
 1263            view.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1265        );
 1266
 1267        view.select_to_end(&SelectToEnd, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1271        );
 1272    });
 1273}
 1274
 1275// TODO: Re-enable this test
 1276#[cfg(target_os = "macos")]
 1277#[gpui::test]
 1278fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1279    init_test(cx, |_| {});
 1280
 1281    let view = cx.add_window(|cx| {
 1282        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1283        build_editor(buffer.clone(), cx)
 1284    });
 1285
 1286    assert_eq!('ⓐ'.len_utf8(), 3);
 1287    assert_eq!('α'.len_utf8(), 2);
 1288
 1289    _ = view.update(cx, |view, cx| {
 1290        view.fold_creases(
 1291            vec![
 1292                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1293                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1294                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1295            ],
 1296            true,
 1297            cx,
 1298        );
 1299        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1300
 1301        view.move_right(&MoveRight, cx);
 1302        assert_eq!(
 1303            view.selections.display_ranges(cx),
 1304            &[empty_range(0, "".len())]
 1305        );
 1306        view.move_right(&MoveRight, cx);
 1307        assert_eq!(
 1308            view.selections.display_ranges(cx),
 1309            &[empty_range(0, "ⓐⓑ".len())]
 1310        );
 1311        view.move_right(&MoveRight, cx);
 1312        assert_eq!(
 1313            view.selections.display_ranges(cx),
 1314            &[empty_range(0, "ⓐⓑ⋯".len())]
 1315        );
 1316
 1317        view.move_down(&MoveDown, cx);
 1318        assert_eq!(
 1319            view.selections.display_ranges(cx),
 1320            &[empty_range(1, "ab⋯e".len())]
 1321        );
 1322        view.move_left(&MoveLeft, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(1, "ab⋯".len())]
 1326        );
 1327        view.move_left(&MoveLeft, cx);
 1328        assert_eq!(
 1329            view.selections.display_ranges(cx),
 1330            &[empty_range(1, "ab".len())]
 1331        );
 1332        view.move_left(&MoveLeft, cx);
 1333        assert_eq!(
 1334            view.selections.display_ranges(cx),
 1335            &[empty_range(1, "a".len())]
 1336        );
 1337
 1338        view.move_down(&MoveDown, cx);
 1339        assert_eq!(
 1340            view.selections.display_ranges(cx),
 1341            &[empty_range(2, "α".len())]
 1342        );
 1343        view.move_right(&MoveRight, cx);
 1344        assert_eq!(
 1345            view.selections.display_ranges(cx),
 1346            &[empty_range(2, "αβ".len())]
 1347        );
 1348        view.move_right(&MoveRight, cx);
 1349        assert_eq!(
 1350            view.selections.display_ranges(cx),
 1351            &[empty_range(2, "αβ⋯".len())]
 1352        );
 1353        view.move_right(&MoveRight, cx);
 1354        assert_eq!(
 1355            view.selections.display_ranges(cx),
 1356            &[empty_range(2, "αβ⋯ε".len())]
 1357        );
 1358
 1359        view.move_up(&MoveUp, cx);
 1360        assert_eq!(
 1361            view.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯e".len())]
 1363        );
 1364        view.move_down(&MoveDown, cx);
 1365        assert_eq!(
 1366            view.selections.display_ranges(cx),
 1367            &[empty_range(2, "αβ⋯ε".len())]
 1368        );
 1369        view.move_up(&MoveUp, cx);
 1370        assert_eq!(
 1371            view.selections.display_ranges(cx),
 1372            &[empty_range(1, "ab⋯e".len())]
 1373        );
 1374
 1375        view.move_up(&MoveUp, cx);
 1376        assert_eq!(
 1377            view.selections.display_ranges(cx),
 1378            &[empty_range(0, "ⓐⓑ".len())]
 1379        );
 1380        view.move_left(&MoveLeft, cx);
 1381        assert_eq!(
 1382            view.selections.display_ranges(cx),
 1383            &[empty_range(0, "".len())]
 1384        );
 1385        view.move_left(&MoveLeft, cx);
 1386        assert_eq!(
 1387            view.selections.display_ranges(cx),
 1388            &[empty_range(0, "".len())]
 1389        );
 1390    });
 1391}
 1392
 1393#[gpui::test]
 1394fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1395    init_test(cx, |_| {});
 1396
 1397    let view = cx.add_window(|cx| {
 1398        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1399        build_editor(buffer.clone(), cx)
 1400    });
 1401    _ = view.update(cx, |view, cx| {
 1402        view.change_selections(None, cx, |s| {
 1403            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1404        });
 1405
 1406        // moving above start of document should move selection to start of document,
 1407        // but the next move down should still be at the original goal_x
 1408        view.move_up(&MoveUp, cx);
 1409        assert_eq!(
 1410            view.selections.display_ranges(cx),
 1411            &[empty_range(0, "".len())]
 1412        );
 1413
 1414        view.move_down(&MoveDown, cx);
 1415        assert_eq!(
 1416            view.selections.display_ranges(cx),
 1417            &[empty_range(1, "abcd".len())]
 1418        );
 1419
 1420        view.move_down(&MoveDown, cx);
 1421        assert_eq!(
 1422            view.selections.display_ranges(cx),
 1423            &[empty_range(2, "αβγ".len())]
 1424        );
 1425
 1426        view.move_down(&MoveDown, cx);
 1427        assert_eq!(
 1428            view.selections.display_ranges(cx),
 1429            &[empty_range(3, "abcd".len())]
 1430        );
 1431
 1432        view.move_down(&MoveDown, cx);
 1433        assert_eq!(
 1434            view.selections.display_ranges(cx),
 1435            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1436        );
 1437
 1438        // moving past end of document should not change goal_x
 1439        view.move_down(&MoveDown, cx);
 1440        assert_eq!(
 1441            view.selections.display_ranges(cx),
 1442            &[empty_range(5, "".len())]
 1443        );
 1444
 1445        view.move_down(&MoveDown, cx);
 1446        assert_eq!(
 1447            view.selections.display_ranges(cx),
 1448            &[empty_range(5, "".len())]
 1449        );
 1450
 1451        view.move_up(&MoveUp, cx);
 1452        assert_eq!(
 1453            view.selections.display_ranges(cx),
 1454            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1455        );
 1456
 1457        view.move_up(&MoveUp, cx);
 1458        assert_eq!(
 1459            view.selections.display_ranges(cx),
 1460            &[empty_range(3, "abcd".len())]
 1461        );
 1462
 1463        view.move_up(&MoveUp, cx);
 1464        assert_eq!(
 1465            view.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468    });
 1469}
 1470
 1471#[gpui::test]
 1472fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1473    init_test(cx, |_| {});
 1474    let move_to_beg = MoveToBeginningOfLine {
 1475        stop_at_soft_wraps: true,
 1476    };
 1477
 1478    let move_to_end = MoveToEndOfLine {
 1479        stop_at_soft_wraps: true,
 1480    };
 1481
 1482    let view = cx.add_window(|cx| {
 1483        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1484        build_editor(buffer, cx)
 1485    });
 1486    _ = view.update(cx, |view, cx| {
 1487        view.change_selections(None, cx, |s| {
 1488            s.select_display_ranges([
 1489                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1490                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1491            ]);
 1492        });
 1493    });
 1494
 1495    _ = view.update(cx, |view, cx| {
 1496        view.move_to_beginning_of_line(&move_to_beg, cx);
 1497        assert_eq!(
 1498            view.selections.display_ranges(cx),
 1499            &[
 1500                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1501                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1502            ]
 1503        );
 1504    });
 1505
 1506    _ = view.update(cx, |view, cx| {
 1507        view.move_to_beginning_of_line(&move_to_beg, cx);
 1508        assert_eq!(
 1509            view.selections.display_ranges(cx),
 1510            &[
 1511                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1512                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1513            ]
 1514        );
 1515    });
 1516
 1517    _ = view.update(cx, |view, cx| {
 1518        view.move_to_beginning_of_line(&move_to_beg, cx);
 1519        assert_eq!(
 1520            view.selections.display_ranges(cx),
 1521            &[
 1522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1523                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1524            ]
 1525        );
 1526    });
 1527
 1528    _ = view.update(cx, |view, cx| {
 1529        view.move_to_end_of_line(&move_to_end, cx);
 1530        assert_eq!(
 1531            view.selections.display_ranges(cx),
 1532            &[
 1533                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1534                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1535            ]
 1536        );
 1537    });
 1538
 1539    // Moving to the end of line again is a no-op.
 1540    _ = view.update(cx, |view, cx| {
 1541        view.move_to_end_of_line(&move_to_end, cx);
 1542        assert_eq!(
 1543            view.selections.display_ranges(cx),
 1544            &[
 1545                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1546                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1547            ]
 1548        );
 1549    });
 1550
 1551    _ = view.update(cx, |view, cx| {
 1552        view.move_left(&MoveLeft, cx);
 1553        view.select_to_beginning_of_line(
 1554            &SelectToBeginningOfLine {
 1555                stop_at_soft_wraps: true,
 1556            },
 1557            cx,
 1558        );
 1559        assert_eq!(
 1560            view.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = view.update(cx, |view, cx| {
 1569        view.select_to_beginning_of_line(
 1570            &SelectToBeginningOfLine {
 1571                stop_at_soft_wraps: true,
 1572            },
 1573            cx,
 1574        );
 1575        assert_eq!(
 1576            view.selections.display_ranges(cx),
 1577            &[
 1578                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1579                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1580            ]
 1581        );
 1582    });
 1583
 1584    _ = view.update(cx, |view, cx| {
 1585        view.select_to_beginning_of_line(
 1586            &SelectToBeginningOfLine {
 1587                stop_at_soft_wraps: true,
 1588            },
 1589            cx,
 1590        );
 1591        assert_eq!(
 1592            view.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1595                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = view.update(cx, |view, cx| {
 1601        view.select_to_end_of_line(
 1602            &SelectToEndOfLine {
 1603                stop_at_soft_wraps: true,
 1604            },
 1605            cx,
 1606        );
 1607        assert_eq!(
 1608            view.selections.display_ranges(cx),
 1609            &[
 1610                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1611                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1612            ]
 1613        );
 1614    });
 1615
 1616    _ = view.update(cx, |view, cx| {
 1617        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1618        assert_eq!(view.display_text(cx), "ab\n  de");
 1619        assert_eq!(
 1620            view.selections.display_ranges(cx),
 1621            &[
 1622                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1623                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1624            ]
 1625        );
 1626    });
 1627
 1628    _ = view.update(cx, |view, cx| {
 1629        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1630        assert_eq!(view.display_text(cx), "\n");
 1631        assert_eq!(
 1632            view.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1636            ]
 1637        );
 1638    });
 1639}
 1640
 1641#[gpui::test]
 1642fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1643    init_test(cx, |_| {});
 1644    let move_to_beg = MoveToBeginningOfLine {
 1645        stop_at_soft_wraps: false,
 1646    };
 1647
 1648    let move_to_end = MoveToEndOfLine {
 1649        stop_at_soft_wraps: false,
 1650    };
 1651
 1652    let view = cx.add_window(|cx| {
 1653        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1654        build_editor(buffer, cx)
 1655    });
 1656
 1657    _ = view.update(cx, |view, cx| {
 1658        view.set_wrap_width(Some(140.0.into()), cx);
 1659
 1660        // We expect the following lines after wrapping
 1661        // ```
 1662        // thequickbrownfox
 1663        // jumpedoverthelazydo
 1664        // gs
 1665        // ```
 1666        // The final `gs` was soft-wrapped onto a new line.
 1667        assert_eq!(
 1668            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1669            view.display_text(cx),
 1670        );
 1671
 1672        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1673        // Start the cursor at the `k` on the first line
 1674        view.change_selections(None, cx, |s| {
 1675            s.select_display_ranges([
 1676                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1677            ]);
 1678        });
 1679
 1680        // Moving to the beginning of the line should put us at the beginning of the line.
 1681        view.move_to_beginning_of_line(&move_to_beg, cx);
 1682        assert_eq!(
 1683            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1684            view.selections.display_ranges(cx)
 1685        );
 1686
 1687        // Moving to the end of the line should put us at the end of the line.
 1688        view.move_to_end_of_line(&move_to_end, cx);
 1689        assert_eq!(
 1690            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1691            view.selections.display_ranges(cx)
 1692        );
 1693
 1694        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1695        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1696        view.change_selections(None, cx, |s| {
 1697            s.select_display_ranges([
 1698                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1699            ]);
 1700        });
 1701
 1702        // Moving to the beginning of the line should put us at the start of the second line of
 1703        // display text, i.e., the `j`.
 1704        view.move_to_beginning_of_line(&move_to_beg, cx);
 1705        assert_eq!(
 1706            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1707            view.selections.display_ranges(cx)
 1708        );
 1709
 1710        // Moving to the beginning of the line again should be a no-op.
 1711        view.move_to_beginning_of_line(&move_to_beg, cx);
 1712        assert_eq!(
 1713            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1714            view.selections.display_ranges(cx)
 1715        );
 1716
 1717        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1718        // next display line.
 1719        view.move_to_end_of_line(&move_to_end, cx);
 1720        assert_eq!(
 1721            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1722            view.selections.display_ranges(cx)
 1723        );
 1724
 1725        // Moving to the end of the line again should be a no-op.
 1726        view.move_to_end_of_line(&move_to_end, cx);
 1727        assert_eq!(
 1728            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1729            view.selections.display_ranges(cx)
 1730        );
 1731    });
 1732}
 1733
 1734#[gpui::test]
 1735fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1736    init_test(cx, |_| {});
 1737
 1738    let view = cx.add_window(|cx| {
 1739        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1740        build_editor(buffer, cx)
 1741    });
 1742    _ = view.update(cx, |view, cx| {
 1743        view.change_selections(None, cx, |s| {
 1744            s.select_display_ranges([
 1745                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1746                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1747            ])
 1748        });
 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_previous_word_start(&MoveToPreviousWordStart, 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_to_next_word_end(&MoveToNextWordEnd, cx);
 1772        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1773
 1774        view.move_right(&MoveRight, cx);
 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_previous_word_start(&SelectToPreviousWordStart, cx);
 1779        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1780
 1781        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1782        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1783    });
 1784}
 1785
 1786#[gpui::test]
 1787fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1788    init_test(cx, |_| {});
 1789
 1790    let view = cx.add_window(|cx| {
 1791        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1792        build_editor(buffer, cx)
 1793    });
 1794
 1795    _ = view.update(cx, |view, cx| {
 1796        view.set_wrap_width(Some(140.0.into()), cx);
 1797        assert_eq!(
 1798            view.display_text(cx),
 1799            "use one::{\n    two::three::\n    four::five\n};"
 1800        );
 1801
 1802        view.change_selections(None, cx, |s| {
 1803            s.select_display_ranges([
 1804                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1805            ]);
 1806        });
 1807
 1808        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1809        assert_eq!(
 1810            view.selections.display_ranges(cx),
 1811            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1812        );
 1813
 1814        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1815        assert_eq!(
 1816            view.selections.display_ranges(cx),
 1817            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1818        );
 1819
 1820        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1821        assert_eq!(
 1822            view.selections.display_ranges(cx),
 1823            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1824        );
 1825
 1826        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1827        assert_eq!(
 1828            view.selections.display_ranges(cx),
 1829            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1830        );
 1831
 1832        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1833        assert_eq!(
 1834            view.selections.display_ranges(cx),
 1835            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1836        );
 1837
 1838        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1839        assert_eq!(
 1840            view.selections.display_ranges(cx),
 1841            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1842        );
 1843    });
 1844}
 1845
 1846#[gpui::test]
 1847async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1848    init_test(cx, |_| {});
 1849    let mut cx = EditorTestContext::new(cx).await;
 1850
 1851    let line_height = cx.editor(|editor, cx| {
 1852        editor
 1853            .style()
 1854            .unwrap()
 1855            .text
 1856            .line_height_in_pixels(cx.rem_size())
 1857    });
 1858    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1859
 1860    cx.set_state(
 1861        &r#"ˇone
 1862        two
 1863
 1864        three
 1865        fourˇ
 1866        five
 1867
 1868        six"#
 1869            .unindent(),
 1870    );
 1871
 1872    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1873    cx.assert_editor_state(
 1874        &r#"one
 1875        two
 1876        ˇ
 1877        three
 1878        four
 1879        five
 1880        ˇ
 1881        six"#
 1882            .unindent(),
 1883    );
 1884
 1885    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1886    cx.assert_editor_state(
 1887        &r#"one
 1888        two
 1889
 1890        three
 1891        four
 1892        five
 1893        ˇ
 1894        sixˇ"#
 1895            .unindent(),
 1896    );
 1897
 1898    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1899    cx.assert_editor_state(
 1900        &r#"one
 1901        two
 1902
 1903        three
 1904        four
 1905        five
 1906
 1907        sixˇ"#
 1908            .unindent(),
 1909    );
 1910
 1911    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1912    cx.assert_editor_state(
 1913        &r#"one
 1914        two
 1915
 1916        three
 1917        four
 1918        five
 1919        ˇ
 1920        six"#
 1921            .unindent(),
 1922    );
 1923
 1924    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1925    cx.assert_editor_state(
 1926        &r#"one
 1927        two
 1928        ˇ
 1929        three
 1930        four
 1931        five
 1932
 1933        six"#
 1934            .unindent(),
 1935    );
 1936
 1937    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1938    cx.assert_editor_state(
 1939        &r#"ˇone
 1940        two
 1941
 1942        three
 1943        four
 1944        five
 1945
 1946        six"#
 1947            .unindent(),
 1948    );
 1949}
 1950
 1951#[gpui::test]
 1952async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1953    init_test(cx, |_| {});
 1954    let mut cx = EditorTestContext::new(cx).await;
 1955    let line_height = cx.editor(|editor, cx| {
 1956        editor
 1957            .style()
 1958            .unwrap()
 1959            .text
 1960            .line_height_in_pixels(cx.rem_size())
 1961    });
 1962    let window = cx.window;
 1963    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1964
 1965    cx.set_state(
 1966        r#"ˇone
 1967        two
 1968        three
 1969        four
 1970        five
 1971        six
 1972        seven
 1973        eight
 1974        nine
 1975        ten
 1976        "#,
 1977    );
 1978
 1979    cx.update_editor(|editor, cx| {
 1980        assert_eq!(
 1981            editor.snapshot(cx).scroll_position(),
 1982            gpui::Point::new(0., 0.)
 1983        );
 1984        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1985        assert_eq!(
 1986            editor.snapshot(cx).scroll_position(),
 1987            gpui::Point::new(0., 3.)
 1988        );
 1989        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1990        assert_eq!(
 1991            editor.snapshot(cx).scroll_position(),
 1992            gpui::Point::new(0., 6.)
 1993        );
 1994        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1995        assert_eq!(
 1996            editor.snapshot(cx).scroll_position(),
 1997            gpui::Point::new(0., 3.)
 1998        );
 1999
 2000        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 2001        assert_eq!(
 2002            editor.snapshot(cx).scroll_position(),
 2003            gpui::Point::new(0., 1.)
 2004        );
 2005        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2006        assert_eq!(
 2007            editor.snapshot(cx).scroll_position(),
 2008            gpui::Point::new(0., 3.)
 2009        );
 2010    });
 2011}
 2012
 2013#[gpui::test]
 2014async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2015    init_test(cx, |_| {});
 2016    let mut cx = EditorTestContext::new(cx).await;
 2017
 2018    let line_height = cx.update_editor(|editor, cx| {
 2019        editor.set_vertical_scroll_margin(2, cx);
 2020        editor
 2021            .style()
 2022            .unwrap()
 2023            .text
 2024            .line_height_in_pixels(cx.rem_size())
 2025    });
 2026    let window = cx.window;
 2027    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2028
 2029    cx.set_state(
 2030        r#"ˇone
 2031            two
 2032            three
 2033            four
 2034            five
 2035            six
 2036            seven
 2037            eight
 2038            nine
 2039            ten
 2040        "#,
 2041    );
 2042    cx.update_editor(|editor, cx| {
 2043        assert_eq!(
 2044            editor.snapshot(cx).scroll_position(),
 2045            gpui::Point::new(0., 0.0)
 2046        );
 2047    });
 2048
 2049    // Add a cursor below the visible area. Since both cursors cannot fit
 2050    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2051    // allows the vertical scroll margin below that cursor.
 2052    cx.update_editor(|editor, cx| {
 2053        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2054            selections.select_ranges([
 2055                Point::new(0, 0)..Point::new(0, 0),
 2056                Point::new(6, 0)..Point::new(6, 0),
 2057            ]);
 2058        })
 2059    });
 2060    cx.update_editor(|editor, cx| {
 2061        assert_eq!(
 2062            editor.snapshot(cx).scroll_position(),
 2063            gpui::Point::new(0., 3.0)
 2064        );
 2065    });
 2066
 2067    // Move down. The editor cursor scrolls down to track the newest cursor.
 2068    cx.update_editor(|editor, cx| {
 2069        editor.move_down(&Default::default(), cx);
 2070    });
 2071    cx.update_editor(|editor, cx| {
 2072        assert_eq!(
 2073            editor.snapshot(cx).scroll_position(),
 2074            gpui::Point::new(0., 4.0)
 2075        );
 2076    });
 2077
 2078    // Add a cursor above the visible area. Since both cursors fit on screen,
 2079    // the editor scrolls to show both.
 2080    cx.update_editor(|editor, cx| {
 2081        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2082            selections.select_ranges([
 2083                Point::new(1, 0)..Point::new(1, 0),
 2084                Point::new(6, 0)..Point::new(6, 0),
 2085            ]);
 2086        })
 2087    });
 2088    cx.update_editor(|editor, cx| {
 2089        assert_eq!(
 2090            editor.snapshot(cx).scroll_position(),
 2091            gpui::Point::new(0., 1.0)
 2092        );
 2093    });
 2094}
 2095
 2096#[gpui::test]
 2097async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2098    init_test(cx, |_| {});
 2099    let mut cx = EditorTestContext::new(cx).await;
 2100
 2101    let line_height = cx.editor(|editor, cx| {
 2102        editor
 2103            .style()
 2104            .unwrap()
 2105            .text
 2106            .line_height_in_pixels(cx.rem_size())
 2107    });
 2108    let window = cx.window;
 2109    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2110    cx.set_state(
 2111        &r#"
 2112        ˇone
 2113        two
 2114        threeˇ
 2115        four
 2116        five
 2117        six
 2118        seven
 2119        eight
 2120        nine
 2121        ten
 2122        "#
 2123        .unindent(),
 2124    );
 2125
 2126    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2127    cx.assert_editor_state(
 2128        &r#"
 2129        one
 2130        two
 2131        three
 2132        ˇfour
 2133        five
 2134        sixˇ
 2135        seven
 2136        eight
 2137        nine
 2138        ten
 2139        "#
 2140        .unindent(),
 2141    );
 2142
 2143    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2144    cx.assert_editor_state(
 2145        &r#"
 2146        one
 2147        two
 2148        three
 2149        four
 2150        five
 2151        six
 2152        ˇseven
 2153        eight
 2154        nineˇ
 2155        ten
 2156        "#
 2157        .unindent(),
 2158    );
 2159
 2160    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2161    cx.assert_editor_state(
 2162        &r#"
 2163        one
 2164        two
 2165        three
 2166        ˇfour
 2167        five
 2168        sixˇ
 2169        seven
 2170        eight
 2171        nine
 2172        ten
 2173        "#
 2174        .unindent(),
 2175    );
 2176
 2177    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2178    cx.assert_editor_state(
 2179        &r#"
 2180        ˇone
 2181        two
 2182        threeˇ
 2183        four
 2184        five
 2185        six
 2186        seven
 2187        eight
 2188        nine
 2189        ten
 2190        "#
 2191        .unindent(),
 2192    );
 2193
 2194    // Test select collapsing
 2195    cx.update_editor(|editor, cx| {
 2196        editor.move_page_down(&MovePageDown::default(), cx);
 2197        editor.move_page_down(&MovePageDown::default(), cx);
 2198        editor.move_page_down(&MovePageDown::default(), cx);
 2199    });
 2200    cx.assert_editor_state(
 2201        &r#"
 2202        one
 2203        two
 2204        three
 2205        four
 2206        five
 2207        six
 2208        seven
 2209        eight
 2210        nine
 2211        ˇten
 2212        ˇ"#
 2213        .unindent(),
 2214    );
 2215}
 2216
 2217#[gpui::test]
 2218async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2219    init_test(cx, |_| {});
 2220    let mut cx = EditorTestContext::new(cx).await;
 2221    cx.set_state("one «two threeˇ» four");
 2222    cx.update_editor(|editor, cx| {
 2223        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2224        assert_eq!(editor.text(cx), " four");
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2230    init_test(cx, |_| {});
 2231
 2232    let view = cx.add_window(|cx| {
 2233        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2234        build_editor(buffer.clone(), cx)
 2235    });
 2236
 2237    _ = view.update(cx, |view, cx| {
 2238        view.change_selections(None, cx, |s| {
 2239            s.select_display_ranges([
 2240                // an empty selection - the preceding word fragment is deleted
 2241                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2242                // characters selected - they are deleted
 2243                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2244            ])
 2245        });
 2246        view.delete_to_previous_word_start(
 2247            &DeleteToPreviousWordStart {
 2248                ignore_newlines: false,
 2249            },
 2250            cx,
 2251        );
 2252        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2253    });
 2254
 2255    _ = view.update(cx, |view, cx| {
 2256        view.change_selections(None, cx, |s| {
 2257            s.select_display_ranges([
 2258                // an empty selection - the following word fragment is deleted
 2259                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2260                // characters selected - they are deleted
 2261                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2262            ])
 2263        });
 2264        view.delete_to_next_word_end(
 2265            &DeleteToNextWordEnd {
 2266                ignore_newlines: false,
 2267            },
 2268            cx,
 2269        );
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2286        ignore_newlines: true,
 2287    };
 2288
 2289    _ = view.update(cx, |view, cx| {
 2290        view.change_selections(None, cx, |s| {
 2291            s.select_display_ranges([
 2292                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2293            ])
 2294        });
 2295        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2296        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2297        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2298        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2299        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2300        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2301        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2302        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2303        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2304        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2305        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2307    });
 2308}
 2309
 2310#[gpui::test]
 2311fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2312    init_test(cx, |_| {});
 2313
 2314    let view = cx.add_window(|cx| {
 2315        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2316        build_editor(buffer.clone(), cx)
 2317    });
 2318    let del_to_next_word_end = DeleteToNextWordEnd {
 2319        ignore_newlines: false,
 2320    };
 2321    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2322        ignore_newlines: true,
 2323    };
 2324
 2325    _ = view.update(cx, |view, cx| {
 2326        view.change_selections(None, cx, |s| {
 2327            s.select_display_ranges([
 2328                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2329            ])
 2330        });
 2331        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2332        assert_eq!(
 2333            view.buffer.read(cx).read(cx).text(),
 2334            "one\n   two\nthree\n   four"
 2335        );
 2336        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2337        assert_eq!(
 2338            view.buffer.read(cx).read(cx).text(),
 2339            "\n   two\nthree\n   four"
 2340        );
 2341        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2342        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2343        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2344        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2345        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2346        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2347        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2348        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2349    });
 2350}
 2351
 2352#[gpui::test]
 2353fn test_newline(cx: &mut TestAppContext) {
 2354    init_test(cx, |_| {});
 2355
 2356    let view = cx.add_window(|cx| {
 2357        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2358        build_editor(buffer.clone(), cx)
 2359    });
 2360
 2361    _ = view.update(cx, |view, cx| {
 2362        view.change_selections(None, cx, |s| {
 2363            s.select_display_ranges([
 2364                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2365                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2366                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2367            ])
 2368        });
 2369
 2370        view.newline(&Newline, cx);
 2371        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2372    });
 2373}
 2374
 2375#[gpui::test]
 2376fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2377    init_test(cx, |_| {});
 2378
 2379    let editor = cx.add_window(|cx| {
 2380        let buffer = MultiBuffer::build_simple(
 2381            "
 2382                a
 2383                b(
 2384                    X
 2385                )
 2386                c(
 2387                    X
 2388                )
 2389            "
 2390            .unindent()
 2391            .as_str(),
 2392            cx,
 2393        );
 2394        let mut editor = build_editor(buffer.clone(), cx);
 2395        editor.change_selections(None, cx, |s| {
 2396            s.select_ranges([
 2397                Point::new(2, 4)..Point::new(2, 5),
 2398                Point::new(5, 4)..Point::new(5, 5),
 2399            ])
 2400        });
 2401        editor
 2402    });
 2403
 2404    _ = editor.update(cx, |editor, cx| {
 2405        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2406        editor.buffer.update(cx, |buffer, cx| {
 2407            buffer.edit(
 2408                [
 2409                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2410                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2411                ],
 2412                None,
 2413                cx,
 2414            );
 2415            assert_eq!(
 2416                buffer.read(cx).text(),
 2417                "
 2418                    a
 2419                    b()
 2420                    c()
 2421                "
 2422                .unindent()
 2423            );
 2424        });
 2425        assert_eq!(
 2426            editor.selections.ranges(cx),
 2427            &[
 2428                Point::new(1, 2)..Point::new(1, 2),
 2429                Point::new(2, 2)..Point::new(2, 2),
 2430            ],
 2431        );
 2432
 2433        editor.newline(&Newline, cx);
 2434        assert_eq!(
 2435            editor.text(cx),
 2436            "
 2437                a
 2438                b(
 2439                )
 2440                c(
 2441                )
 2442            "
 2443            .unindent()
 2444        );
 2445
 2446        // The selections are moved after the inserted newlines
 2447        assert_eq!(
 2448            editor.selections.ranges(cx),
 2449            &[
 2450                Point::new(2, 0)..Point::new(2, 0),
 2451                Point::new(4, 0)..Point::new(4, 0),
 2452            ],
 2453        );
 2454    });
 2455}
 2456
 2457#[gpui::test]
 2458async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2459    init_test(cx, |settings| {
 2460        settings.defaults.tab_size = NonZeroU32::new(4)
 2461    });
 2462
 2463    let language = Arc::new(
 2464        Language::new(
 2465            LanguageConfig::default(),
 2466            Some(tree_sitter_rust::LANGUAGE.into()),
 2467        )
 2468        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2469        .unwrap(),
 2470    );
 2471
 2472    let mut cx = EditorTestContext::new(cx).await;
 2473    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2474    cx.set_state(indoc! {"
 2475        const a: ˇA = (
 2476 2477                «const_functionˇ»(ˇ),
 2478                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2479 2480        ˇ);ˇ
 2481    "});
 2482
 2483    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2484    cx.assert_editor_state(indoc! {"
 2485        ˇ
 2486        const a: A = (
 2487            ˇ
 2488            (
 2489                ˇ
 2490                ˇ
 2491                const_function(),
 2492                ˇ
 2493                ˇ
 2494                ˇ
 2495                ˇ
 2496                something_else,
 2497                ˇ
 2498            )
 2499            ˇ
 2500            ˇ
 2501        );
 2502    "});
 2503}
 2504
 2505#[gpui::test]
 2506async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2507    init_test(cx, |settings| {
 2508        settings.defaults.tab_size = NonZeroU32::new(4)
 2509    });
 2510
 2511    let language = Arc::new(
 2512        Language::new(
 2513            LanguageConfig::default(),
 2514            Some(tree_sitter_rust::LANGUAGE.into()),
 2515        )
 2516        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2517        .unwrap(),
 2518    );
 2519
 2520    let mut cx = EditorTestContext::new(cx).await;
 2521    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2522    cx.set_state(indoc! {"
 2523        const a: ˇA = (
 2524 2525                «const_functionˇ»(ˇ),
 2526                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2527 2528        ˇ);ˇ
 2529    "});
 2530
 2531    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2532    cx.assert_editor_state(indoc! {"
 2533        const a: A = (
 2534            ˇ
 2535            (
 2536                ˇ
 2537                const_function(),
 2538                ˇ
 2539                ˇ
 2540                something_else,
 2541                ˇ
 2542                ˇ
 2543                ˇ
 2544                ˇ
 2545            )
 2546            ˇ
 2547        );
 2548        ˇ
 2549        ˇ
 2550    "});
 2551}
 2552
 2553#[gpui::test]
 2554async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2555    init_test(cx, |settings| {
 2556        settings.defaults.tab_size = NonZeroU32::new(4)
 2557    });
 2558
 2559    let language = Arc::new(Language::new(
 2560        LanguageConfig {
 2561            line_comments: vec!["//".into()],
 2562            ..LanguageConfig::default()
 2563        },
 2564        None,
 2565    ));
 2566    {
 2567        let mut cx = EditorTestContext::new(cx).await;
 2568        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2569        cx.set_state(indoc! {"
 2570        // Fooˇ
 2571    "});
 2572
 2573        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2574        cx.assert_editor_state(indoc! {"
 2575        // Foo
 2576        //ˇ
 2577    "});
 2578        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2579        cx.set_state(indoc! {"
 2580        ˇ// Foo
 2581    "});
 2582        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2583        cx.assert_editor_state(indoc! {"
 2584
 2585        ˇ// Foo
 2586    "});
 2587    }
 2588    // Ensure that comment continuations can be disabled.
 2589    update_test_language_settings(cx, |settings| {
 2590        settings.defaults.extend_comment_on_newline = Some(false);
 2591    });
 2592    let mut cx = EditorTestContext::new(cx).await;
 2593    cx.set_state(indoc! {"
 2594        // Fooˇ
 2595    "});
 2596    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2597    cx.assert_editor_state(indoc! {"
 2598        // Foo
 2599        ˇ
 2600    "});
 2601}
 2602
 2603#[gpui::test]
 2604fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2605    init_test(cx, |_| {});
 2606
 2607    let editor = cx.add_window(|cx| {
 2608        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2609        let mut editor = build_editor(buffer.clone(), cx);
 2610        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2611        editor
 2612    });
 2613
 2614    _ = editor.update(cx, |editor, cx| {
 2615        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2616        editor.buffer.update(cx, |buffer, cx| {
 2617            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2618            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2619        });
 2620        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2621
 2622        editor.insert("Z", cx);
 2623        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2624
 2625        // The selections are moved after the inserted characters
 2626        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2627    });
 2628}
 2629
 2630#[gpui::test]
 2631async fn test_tab(cx: &mut gpui::TestAppContext) {
 2632    init_test(cx, |settings| {
 2633        settings.defaults.tab_size = NonZeroU32::new(3)
 2634    });
 2635
 2636    let mut cx = EditorTestContext::new(cx).await;
 2637    cx.set_state(indoc! {"
 2638        ˇabˇc
 2639        ˇ🏀ˇ🏀ˇefg
 2640 2641    "});
 2642    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2643    cx.assert_editor_state(indoc! {"
 2644           ˇab ˇc
 2645           ˇ🏀  ˇ🏀  ˇefg
 2646        d  ˇ
 2647    "});
 2648
 2649    cx.set_state(indoc! {"
 2650        a
 2651        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655        a
 2656           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2657    "});
 2658}
 2659
 2660#[gpui::test]
 2661async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2662    init_test(cx, |_| {});
 2663
 2664    let mut cx = EditorTestContext::new(cx).await;
 2665    let language = Arc::new(
 2666        Language::new(
 2667            LanguageConfig::default(),
 2668            Some(tree_sitter_rust::LANGUAGE.into()),
 2669        )
 2670        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2671        .unwrap(),
 2672    );
 2673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2674
 2675    // cursors that are already at the suggested indent level insert
 2676    // a soft tab. cursors that are to the left of the suggested indent
 2677    // auto-indent their line.
 2678    cx.set_state(indoc! {"
 2679        ˇ
 2680        const a: B = (
 2681            c(
 2682                d(
 2683        ˇ
 2684                )
 2685        ˇ
 2686        ˇ    )
 2687        );
 2688    "});
 2689    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2690    cx.assert_editor_state(indoc! {"
 2691            ˇ
 2692        const a: B = (
 2693            c(
 2694                d(
 2695                    ˇ
 2696                )
 2697                ˇ
 2698            ˇ)
 2699        );
 2700    "});
 2701
 2702    // handle auto-indent when there are multiple cursors on the same line
 2703    cx.set_state(indoc! {"
 2704        const a: B = (
 2705            c(
 2706        ˇ    ˇ
 2707        ˇ    )
 2708        );
 2709    "});
 2710    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2711    cx.assert_editor_state(indoc! {"
 2712        const a: B = (
 2713            c(
 2714                ˇ
 2715            ˇ)
 2716        );
 2717    "});
 2718}
 2719
 2720#[gpui::test]
 2721async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2722    init_test(cx, |settings| {
 2723        settings.defaults.tab_size = NonZeroU32::new(4)
 2724    });
 2725
 2726    let language = Arc::new(
 2727        Language::new(
 2728            LanguageConfig::default(),
 2729            Some(tree_sitter_rust::LANGUAGE.into()),
 2730        )
 2731        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2732        .unwrap(),
 2733    );
 2734
 2735    let mut cx = EditorTestContext::new(cx).await;
 2736    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2737    cx.set_state(indoc! {"
 2738        fn a() {
 2739            if b {
 2740        \t ˇc
 2741            }
 2742        }
 2743    "});
 2744
 2745    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2746    cx.assert_editor_state(indoc! {"
 2747        fn a() {
 2748            if b {
 2749                ˇc
 2750            }
 2751        }
 2752    "});
 2753}
 2754
 2755#[gpui::test]
 2756async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2757    init_test(cx, |settings| {
 2758        settings.defaults.tab_size = NonZeroU32::new(4);
 2759    });
 2760
 2761    let mut cx = EditorTestContext::new(cx).await;
 2762
 2763    cx.set_state(indoc! {"
 2764          «oneˇ» «twoˇ»
 2765        three
 2766         four
 2767    "});
 2768    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770            «oneˇ» «twoˇ»
 2771        three
 2772         four
 2773    "});
 2774
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        «oneˇ» «twoˇ»
 2778        three
 2779         four
 2780    "});
 2781
 2782    // select across line ending
 2783    cx.set_state(indoc! {"
 2784        one two
 2785        t«hree
 2786        ˇ» four
 2787    "});
 2788    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        one two
 2791            t«hree
 2792        ˇ» four
 2793    "});
 2794
 2795    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2796    cx.assert_editor_state(indoc! {"
 2797        one two
 2798        t«hree
 2799        ˇ» four
 2800    "});
 2801
 2802    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2803    cx.set_state(indoc! {"
 2804        one two
 2805        ˇthree
 2806            four
 2807    "});
 2808    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2809    cx.assert_editor_state(indoc! {"
 2810        one two
 2811            ˇthree
 2812            four
 2813    "});
 2814
 2815    cx.set_state(indoc! {"
 2816        one two
 2817        ˇ    three
 2818            four
 2819    "});
 2820    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2821    cx.assert_editor_state(indoc! {"
 2822        one two
 2823        ˇthree
 2824            four
 2825    "});
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.hard_tabs = Some(true);
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835
 2836    // select two ranges on one line
 2837    cx.set_state(indoc! {"
 2838        «oneˇ» «twoˇ»
 2839        three
 2840        four
 2841    "});
 2842    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844        \t«oneˇ» «twoˇ»
 2845        three
 2846        four
 2847    "});
 2848    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2849    cx.assert_editor_state(indoc! {"
 2850        \t\t«oneˇ» «twoˇ»
 2851        three
 2852        four
 2853    "});
 2854    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2855    cx.assert_editor_state(indoc! {"
 2856        \t«oneˇ» «twoˇ»
 2857        three
 2858        four
 2859    "});
 2860    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2861    cx.assert_editor_state(indoc! {"
 2862        «oneˇ» «twoˇ»
 2863        three
 2864        four
 2865    "});
 2866
 2867    // select across a line ending
 2868    cx.set_state(indoc! {"
 2869        one two
 2870        t«hree
 2871        ˇ»four
 2872    "});
 2873    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2874    cx.assert_editor_state(indoc! {"
 2875        one two
 2876        \tt«hree
 2877        ˇ»four
 2878    "});
 2879    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2880    cx.assert_editor_state(indoc! {"
 2881        one two
 2882        \t\tt«hree
 2883        ˇ»four
 2884    "});
 2885    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2886    cx.assert_editor_state(indoc! {"
 2887        one two
 2888        \tt«hree
 2889        ˇ»four
 2890    "});
 2891    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2892    cx.assert_editor_state(indoc! {"
 2893        one two
 2894        t«hree
 2895        ˇ»four
 2896    "});
 2897
 2898    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2899    cx.set_state(indoc! {"
 2900        one two
 2901        ˇthree
 2902        four
 2903    "});
 2904    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        one two
 2907        ˇthree
 2908        four
 2909    "});
 2910    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        one two
 2913        \tˇthree
 2914        four
 2915    "});
 2916    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2917    cx.assert_editor_state(indoc! {"
 2918        one two
 2919        ˇthree
 2920        four
 2921    "});
 2922}
 2923
 2924#[gpui::test]
 2925fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2926    init_test(cx, |settings| {
 2927        settings.languages.extend([
 2928            (
 2929                "TOML".into(),
 2930                LanguageSettingsContent {
 2931                    tab_size: NonZeroU32::new(2),
 2932                    ..Default::default()
 2933                },
 2934            ),
 2935            (
 2936                "Rust".into(),
 2937                LanguageSettingsContent {
 2938                    tab_size: NonZeroU32::new(4),
 2939                    ..Default::default()
 2940                },
 2941            ),
 2942        ]);
 2943    });
 2944
 2945    let toml_language = Arc::new(Language::new(
 2946        LanguageConfig {
 2947            name: "TOML".into(),
 2948            ..Default::default()
 2949        },
 2950        None,
 2951    ));
 2952    let rust_language = Arc::new(Language::new(
 2953        LanguageConfig {
 2954            name: "Rust".into(),
 2955            ..Default::default()
 2956        },
 2957        None,
 2958    ));
 2959
 2960    let toml_buffer =
 2961        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2962    let rust_buffer = cx.new_model(|cx| {
 2963        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2964    });
 2965    let multibuffer = cx.new_model(|cx| {
 2966        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2967        multibuffer.push_excerpts(
 2968            toml_buffer.clone(),
 2969            [ExcerptRange {
 2970                context: Point::new(0, 0)..Point::new(2, 0),
 2971                primary: None,
 2972            }],
 2973            cx,
 2974        );
 2975        multibuffer.push_excerpts(
 2976            rust_buffer.clone(),
 2977            [ExcerptRange {
 2978                context: Point::new(0, 0)..Point::new(1, 0),
 2979                primary: None,
 2980            }],
 2981            cx,
 2982        );
 2983        multibuffer
 2984    });
 2985
 2986    cx.add_window(|cx| {
 2987        let mut editor = build_editor(multibuffer, cx);
 2988
 2989        assert_eq!(
 2990            editor.text(cx),
 2991            indoc! {"
 2992                a = 1
 2993                b = 2
 2994
 2995                const c: usize = 3;
 2996            "}
 2997        );
 2998
 2999        select_ranges(
 3000            &mut editor,
 3001            indoc! {"
 3002                «aˇ» = 1
 3003                b = 2
 3004
 3005                «const c:ˇ» usize = 3;
 3006            "},
 3007            cx,
 3008        );
 3009
 3010        editor.tab(&Tab, cx);
 3011        assert_text_with_selections(
 3012            &mut editor,
 3013            indoc! {"
 3014                  «aˇ» = 1
 3015                b = 2
 3016
 3017                    «const c:ˇ» usize = 3;
 3018            "},
 3019            cx,
 3020        );
 3021        editor.tab_prev(&TabPrev, cx);
 3022        assert_text_with_selections(
 3023            &mut editor,
 3024            indoc! {"
 3025                «aˇ» = 1
 3026                b = 2
 3027
 3028                «const c:ˇ» usize = 3;
 3029            "},
 3030            cx,
 3031        );
 3032
 3033        editor
 3034    });
 3035}
 3036
 3037#[gpui::test]
 3038async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3039    init_test(cx, |_| {});
 3040
 3041    let mut cx = EditorTestContext::new(cx).await;
 3042
 3043    // Basic backspace
 3044    cx.set_state(indoc! {"
 3045        onˇe two three
 3046        fou«rˇ» five six
 3047        seven «ˇeight nine
 3048        »ten
 3049    "});
 3050    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        oˇe two three
 3053        fouˇ five six
 3054        seven ˇten
 3055    "});
 3056
 3057    // Test backspace inside and around indents
 3058    cx.set_state(indoc! {"
 3059        zero
 3060            ˇone
 3061                ˇtwo
 3062            ˇ ˇ ˇ  three
 3063        ˇ  ˇ  four
 3064    "});
 3065    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        zero
 3068        ˇone
 3069            ˇtwo
 3070        ˇ  threeˇ  four
 3071    "});
 3072
 3073    // Test backspace with line_mode set to true
 3074    cx.update_editor(|e, _| e.selections.line_mode = true);
 3075    cx.set_state(indoc! {"
 3076        The ˇquick ˇbrown
 3077        fox jumps over
 3078        the lazy dog
 3079        ˇThe qu«ick bˇ»rown"});
 3080    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3081    cx.assert_editor_state(indoc! {"
 3082        ˇfox jumps over
 3083        the lazy dogˇ"});
 3084}
 3085
 3086#[gpui::test]
 3087async fn test_delete(cx: &mut gpui::TestAppContext) {
 3088    init_test(cx, |_| {});
 3089
 3090    let mut cx = EditorTestContext::new(cx).await;
 3091    cx.set_state(indoc! {"
 3092        onˇe two three
 3093        fou«rˇ» five six
 3094        seven «ˇeight nine
 3095        »ten
 3096    "});
 3097    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        onˇ two three
 3100        fouˇ five six
 3101        seven ˇten
 3102    "});
 3103
 3104    // Test backspace with line_mode set to true
 3105    cx.update_editor(|e, _| e.selections.line_mode = true);
 3106    cx.set_state(indoc! {"
 3107        The ˇquick ˇbrown
 3108        fox «ˇjum»ps over
 3109        the lazy dog
 3110        ˇThe qu«ick bˇ»rown"});
 3111    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3112    cx.assert_editor_state("ˇthe lazy dogˇ");
 3113}
 3114
 3115#[gpui::test]
 3116fn test_delete_line(cx: &mut TestAppContext) {
 3117    init_test(cx, |_| {});
 3118
 3119    let view = cx.add_window(|cx| {
 3120        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3121        build_editor(buffer, cx)
 3122    });
 3123    _ = view.update(cx, |view, cx| {
 3124        view.change_selections(None, cx, |s| {
 3125            s.select_display_ranges([
 3126                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3127                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3128                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3129            ])
 3130        });
 3131        view.delete_line(&DeleteLine, cx);
 3132        assert_eq!(view.display_text(cx), "ghi");
 3133        assert_eq!(
 3134            view.selections.display_ranges(cx),
 3135            vec![
 3136                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3137                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3138            ]
 3139        );
 3140    });
 3141
 3142    let view = cx.add_window(|cx| {
 3143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3144        build_editor(buffer, cx)
 3145    });
 3146    _ = view.update(cx, |view, cx| {
 3147        view.change_selections(None, cx, |s| {
 3148            s.select_display_ranges([
 3149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3150            ])
 3151        });
 3152        view.delete_line(&DeleteLine, cx);
 3153        assert_eq!(view.display_text(cx), "ghi\n");
 3154        assert_eq!(
 3155            view.selections.display_ranges(cx),
 3156            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3157        );
 3158    });
 3159}
 3160
 3161#[gpui::test]
 3162fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3163    init_test(cx, |_| {});
 3164
 3165    cx.add_window(|cx| {
 3166        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3167        let mut editor = build_editor(buffer.clone(), cx);
 3168        let buffer = buffer.read(cx).as_singleton().unwrap();
 3169
 3170        assert_eq!(
 3171            editor.selections.ranges::<Point>(cx),
 3172            &[Point::new(0, 0)..Point::new(0, 0)]
 3173        );
 3174
 3175        // When on single line, replace newline at end by space
 3176        editor.join_lines(&JoinLines, cx);
 3177        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3178        assert_eq!(
 3179            editor.selections.ranges::<Point>(cx),
 3180            &[Point::new(0, 3)..Point::new(0, 3)]
 3181        );
 3182
 3183        // When multiple lines are selected, remove newlines that are spanned by the selection
 3184        editor.change_selections(None, cx, |s| {
 3185            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3186        });
 3187        editor.join_lines(&JoinLines, cx);
 3188        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3189        assert_eq!(
 3190            editor.selections.ranges::<Point>(cx),
 3191            &[Point::new(0, 11)..Point::new(0, 11)]
 3192        );
 3193
 3194        // Undo should be transactional
 3195        editor.undo(&Undo, cx);
 3196        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3197        assert_eq!(
 3198            editor.selections.ranges::<Point>(cx),
 3199            &[Point::new(0, 5)..Point::new(2, 2)]
 3200        );
 3201
 3202        // When joining an empty line don't insert a space
 3203        editor.change_selections(None, cx, |s| {
 3204            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3205        });
 3206        editor.join_lines(&JoinLines, cx);
 3207        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3208        assert_eq!(
 3209            editor.selections.ranges::<Point>(cx),
 3210            [Point::new(2, 3)..Point::new(2, 3)]
 3211        );
 3212
 3213        // We can remove trailing newlines
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3216        assert_eq!(
 3217            editor.selections.ranges::<Point>(cx),
 3218            [Point::new(2, 3)..Point::new(2, 3)]
 3219        );
 3220
 3221        // We don't blow up on the last line
 3222        editor.join_lines(&JoinLines, cx);
 3223        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3224        assert_eq!(
 3225            editor.selections.ranges::<Point>(cx),
 3226            [Point::new(2, 3)..Point::new(2, 3)]
 3227        );
 3228
 3229        // reset to test indentation
 3230        editor.buffer.update(cx, |buffer, cx| {
 3231            buffer.edit(
 3232                [
 3233                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3234                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3235                ],
 3236                None,
 3237                cx,
 3238            )
 3239        });
 3240
 3241        // We remove any leading spaces
 3242        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3243        editor.change_selections(None, cx, |s| {
 3244            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3245        });
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3248
 3249        // We don't insert a space for a line containing only spaces
 3250        editor.join_lines(&JoinLines, cx);
 3251        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3252
 3253        // We ignore any leading tabs
 3254        editor.join_lines(&JoinLines, cx);
 3255        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3256
 3257        editor
 3258    });
 3259}
 3260
 3261#[gpui::test]
 3262fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3263    init_test(cx, |_| {});
 3264
 3265    cx.add_window(|cx| {
 3266        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3267        let mut editor = build_editor(buffer.clone(), cx);
 3268        let buffer = buffer.read(cx).as_singleton().unwrap();
 3269
 3270        editor.change_selections(None, cx, |s| {
 3271            s.select_ranges([
 3272                Point::new(0, 2)..Point::new(1, 1),
 3273                Point::new(1, 2)..Point::new(1, 2),
 3274                Point::new(3, 1)..Point::new(3, 2),
 3275            ])
 3276        });
 3277
 3278        editor.join_lines(&JoinLines, cx);
 3279        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3280
 3281        assert_eq!(
 3282            editor.selections.ranges::<Point>(cx),
 3283            [
 3284                Point::new(0, 7)..Point::new(0, 7),
 3285                Point::new(1, 3)..Point::new(1, 3)
 3286            ]
 3287        );
 3288        editor
 3289    });
 3290}
 3291
 3292#[gpui::test]
 3293async fn test_join_lines_with_git_diff_base(
 3294    executor: BackgroundExecutor,
 3295    cx: &mut gpui::TestAppContext,
 3296) {
 3297    init_test(cx, |_| {});
 3298
 3299    let mut cx = EditorTestContext::new(cx).await;
 3300
 3301    let diff_base = r#"
 3302        Line 0
 3303        Line 1
 3304        Line 2
 3305        Line 3
 3306        "#
 3307    .unindent();
 3308
 3309    cx.set_state(
 3310        &r#"
 3311        ˇLine 0
 3312        Line 1
 3313        Line 2
 3314        Line 3
 3315        "#
 3316        .unindent(),
 3317    );
 3318
 3319    cx.set_diff_base(&diff_base);
 3320    executor.run_until_parked();
 3321
 3322    // Join lines
 3323    cx.update_editor(|editor, cx| {
 3324        editor.join_lines(&JoinLines, cx);
 3325    });
 3326    executor.run_until_parked();
 3327
 3328    cx.assert_editor_state(
 3329        &r#"
 3330        Line 0ˇ Line 1
 3331        Line 2
 3332        Line 3
 3333        "#
 3334        .unindent(),
 3335    );
 3336    // Join again
 3337    cx.update_editor(|editor, cx| {
 3338        editor.join_lines(&JoinLines, cx);
 3339    });
 3340    executor.run_until_parked();
 3341
 3342    cx.assert_editor_state(
 3343        &r#"
 3344        Line 0 Line 1ˇ Line 2
 3345        Line 3
 3346        "#
 3347        .unindent(),
 3348    );
 3349}
 3350
 3351#[gpui::test]
 3352async fn test_custom_newlines_cause_no_false_positive_diffs(
 3353    executor: BackgroundExecutor,
 3354    cx: &mut gpui::TestAppContext,
 3355) {
 3356    init_test(cx, |_| {});
 3357    let mut cx = EditorTestContext::new(cx).await;
 3358    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3359    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3360    executor.run_until_parked();
 3361
 3362    cx.update_editor(|editor, cx| {
 3363        let snapshot = editor.snapshot(cx);
 3364        assert_eq!(
 3365            snapshot
 3366                .diff_map
 3367                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
 3368                .collect::<Vec<_>>(),
 3369            Vec::new(),
 3370            "Should not have any diffs for files with custom newlines"
 3371        );
 3372    });
 3373}
 3374
 3375#[gpui::test]
 3376async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3377    init_test(cx, |_| {});
 3378
 3379    let mut cx = EditorTestContext::new(cx).await;
 3380
 3381    // Test sort_lines_case_insensitive()
 3382    cx.set_state(indoc! {"
 3383        «z
 3384        y
 3385        x
 3386        Z
 3387        Y
 3388        Xˇ»
 3389    "});
 3390    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3391    cx.assert_editor_state(indoc! {"
 3392        «x
 3393        X
 3394        y
 3395        Y
 3396        z
 3397        Zˇ»
 3398    "});
 3399
 3400    // Test reverse_lines()
 3401    cx.set_state(indoc! {"
 3402        «5
 3403        4
 3404        3
 3405        2
 3406        1ˇ»
 3407    "});
 3408    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3409    cx.assert_editor_state(indoc! {"
 3410        «1
 3411        2
 3412        3
 3413        4
 3414        5ˇ»
 3415    "});
 3416
 3417    // Skip testing shuffle_line()
 3418
 3419    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3420    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3421
 3422    // Don't manipulate when cursor is on single line, but expand the selection
 3423    cx.set_state(indoc! {"
 3424        ddˇdd
 3425        ccc
 3426        bb
 3427        a
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «ddddˇ»
 3432        ccc
 3433        bb
 3434        a
 3435    "});
 3436
 3437    // Basic manipulate case
 3438    // Start selection moves to column 0
 3439    // End of selection shrinks to fit shorter line
 3440    cx.set_state(indoc! {"
 3441        dd«d
 3442        ccc
 3443        bb
 3444        aaaaaˇ»
 3445    "});
 3446    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3447    cx.assert_editor_state(indoc! {"
 3448        «aaaaa
 3449        bb
 3450        ccc
 3451        dddˇ»
 3452    "});
 3453
 3454    // Manipulate case with newlines
 3455    cx.set_state(indoc! {"
 3456        dd«d
 3457        ccc
 3458
 3459        bb
 3460        aaaaa
 3461
 3462        ˇ»
 3463    "});
 3464    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3465    cx.assert_editor_state(indoc! {"
 3466        «
 3467
 3468        aaaaa
 3469        bb
 3470        ccc
 3471        dddˇ»
 3472
 3473    "});
 3474
 3475    // Adding new line
 3476    cx.set_state(indoc! {"
 3477        aa«a
 3478        bbˇ»b
 3479    "});
 3480    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3481    cx.assert_editor_state(indoc! {"
 3482        «aaa
 3483        bbb
 3484        added_lineˇ»
 3485    "});
 3486
 3487    // Removing line
 3488    cx.set_state(indoc! {"
 3489        aa«a
 3490        bbbˇ»
 3491    "});
 3492    cx.update_editor(|e, cx| {
 3493        e.manipulate_lines(cx, |lines| {
 3494            lines.pop();
 3495        })
 3496    });
 3497    cx.assert_editor_state(indoc! {"
 3498        «aaaˇ»
 3499    "});
 3500
 3501    // Removing all lines
 3502    cx.set_state(indoc! {"
 3503        aa«a
 3504        bbbˇ»
 3505    "});
 3506    cx.update_editor(|e, cx| {
 3507        e.manipulate_lines(cx, |lines| {
 3508            lines.drain(..);
 3509        })
 3510    });
 3511    cx.assert_editor_state(indoc! {"
 3512        ˇ
 3513    "});
 3514}
 3515
 3516#[gpui::test]
 3517async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3518    init_test(cx, |_| {});
 3519
 3520    let mut cx = EditorTestContext::new(cx).await;
 3521
 3522    // Consider continuous selection as single selection
 3523    cx.set_state(indoc! {"
 3524        Aaa«aa
 3525        cˇ»c«c
 3526        bb
 3527        aaaˇ»aa
 3528    "});
 3529    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3530    cx.assert_editor_state(indoc! {"
 3531        «Aaaaa
 3532        ccc
 3533        bb
 3534        aaaaaˇ»
 3535    "});
 3536
 3537    cx.set_state(indoc! {"
 3538        Aaa«aa
 3539        cˇ»c«c
 3540        bb
 3541        aaaˇ»aa
 3542    "});
 3543    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3544    cx.assert_editor_state(indoc! {"
 3545        «Aaaaa
 3546        ccc
 3547        bbˇ»
 3548    "});
 3549
 3550    // Consider non continuous selection as distinct dedup operations
 3551    cx.set_state(indoc! {"
 3552        «aaaaa
 3553        bb
 3554        aaaaa
 3555        aaaaaˇ»
 3556
 3557        aaa«aaˇ»
 3558    "});
 3559    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3560    cx.assert_editor_state(indoc! {"
 3561        «aaaaa
 3562        bbˇ»
 3563
 3564        «aaaaaˇ»
 3565    "});
 3566}
 3567
 3568#[gpui::test]
 3569async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3570    init_test(cx, |_| {});
 3571
 3572    let mut cx = EditorTestContext::new(cx).await;
 3573
 3574    cx.set_state(indoc! {"
 3575        «Aaa
 3576        aAa
 3577        Aaaˇ»
 3578    "});
 3579    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «Aaa
 3582        aAaˇ»
 3583    "});
 3584
 3585    cx.set_state(indoc! {"
 3586        «Aaa
 3587        aAa
 3588        aaAˇ»
 3589    "});
 3590    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3591    cx.assert_editor_state(indoc! {"
 3592        «Aaaˇ»
 3593    "});
 3594}
 3595
 3596#[gpui::test]
 3597async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3598    init_test(cx, |_| {});
 3599
 3600    let mut cx = EditorTestContext::new(cx).await;
 3601
 3602    // Manipulate with multiple selections on a single line
 3603    cx.set_state(indoc! {"
 3604        dd«dd
 3605        cˇ»c«c
 3606        bb
 3607        aaaˇ»aa
 3608    "});
 3609    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3610    cx.assert_editor_state(indoc! {"
 3611        «aaaaa
 3612        bb
 3613        ccc
 3614        ddddˇ»
 3615    "});
 3616
 3617    // Manipulate with multiple disjoin selections
 3618    cx.set_state(indoc! {"
 3619 3620        4
 3621        3
 3622        2
 3623        1ˇ»
 3624
 3625        dd«dd
 3626        ccc
 3627        bb
 3628        aaaˇ»aa
 3629    "});
 3630    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3631    cx.assert_editor_state(indoc! {"
 3632        «1
 3633        2
 3634        3
 3635        4
 3636        5ˇ»
 3637
 3638        «aaaaa
 3639        bb
 3640        ccc
 3641        ddddˇ»
 3642    "});
 3643
 3644    // Adding lines on each selection
 3645    cx.set_state(indoc! {"
 3646 3647        1ˇ»
 3648
 3649        bb«bb
 3650        aaaˇ»aa
 3651    "});
 3652    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3653    cx.assert_editor_state(indoc! {"
 3654        «2
 3655        1
 3656        added lineˇ»
 3657
 3658        «bbbb
 3659        aaaaa
 3660        added lineˇ»
 3661    "});
 3662
 3663    // Removing lines on each selection
 3664    cx.set_state(indoc! {"
 3665 3666        1ˇ»
 3667
 3668        bb«bb
 3669        aaaˇ»aa
 3670    "});
 3671    cx.update_editor(|e, cx| {
 3672        e.manipulate_lines(cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «2ˇ»
 3678
 3679        «bbbbˇ»
 3680    "});
 3681}
 3682
 3683#[gpui::test]
 3684async fn test_manipulate_text(cx: &mut TestAppContext) {
 3685    init_test(cx, |_| {});
 3686
 3687    let mut cx = EditorTestContext::new(cx).await;
 3688
 3689    // Test convert_to_upper_case()
 3690    cx.set_state(indoc! {"
 3691        «hello worldˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «HELLO WORLDˇ»
 3696    "});
 3697
 3698    // Test convert_to_lower_case()
 3699    cx.set_state(indoc! {"
 3700        «HELLO WORLDˇ»
 3701    "});
 3702    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3703    cx.assert_editor_state(indoc! {"
 3704        «hello worldˇ»
 3705    "});
 3706
 3707    // Test multiple line, single selection case
 3708    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3709    cx.set_state(indoc! {"
 3710        «The quick brown
 3711        fox jumps over
 3712        the lazy dogˇ»
 3713    "});
 3714    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3715    cx.assert_editor_state(indoc! {"
 3716        «The Quick Brown
 3717        Fox Jumps Over
 3718        The Lazy Dogˇ»
 3719    "});
 3720
 3721    // Test multiple line, single selection case
 3722    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3723    cx.set_state(indoc! {"
 3724        «The quick brown
 3725        fox jumps over
 3726        the lazy dogˇ»
 3727    "});
 3728    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3729    cx.assert_editor_state(indoc! {"
 3730        «TheQuickBrown
 3731        FoxJumpsOver
 3732        TheLazyDogˇ»
 3733    "});
 3734
 3735    // From here on out, test more complex cases of manipulate_text()
 3736
 3737    // Test no selection case - should affect words cursors are in
 3738    // Cursor at beginning, middle, and end of word
 3739    cx.set_state(indoc! {"
 3740        ˇhello big beauˇtiful worldˇ
 3741    "});
 3742    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3743    cx.assert_editor_state(indoc! {"
 3744        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3745    "});
 3746
 3747    // Test multiple selections on a single line and across multiple lines
 3748    cx.set_state(indoc! {"
 3749        «Theˇ» quick «brown
 3750        foxˇ» jumps «overˇ»
 3751        the «lazyˇ» dog
 3752    "});
 3753    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3754    cx.assert_editor_state(indoc! {"
 3755        «THEˇ» quick «BROWN
 3756        FOXˇ» jumps «OVERˇ»
 3757        the «LAZYˇ» dog
 3758    "});
 3759
 3760    // Test case where text length grows
 3761    cx.set_state(indoc! {"
 3762        «tschüߡ»
 3763    "});
 3764    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3765    cx.assert_editor_state(indoc! {"
 3766        «TSCHÜSSˇ»
 3767    "});
 3768
 3769    // Test to make sure we don't crash when text shrinks
 3770    cx.set_state(indoc! {"
 3771        aaa_bbbˇ
 3772    "});
 3773    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3774    cx.assert_editor_state(indoc! {"
 3775        «aaaBbbˇ»
 3776    "});
 3777
 3778    // Test to make sure we all aware of the fact that each word can grow and shrink
 3779    // Final selections should be aware of this fact
 3780    cx.set_state(indoc! {"
 3781        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3782    "});
 3783    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3784    cx.assert_editor_state(indoc! {"
 3785        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3786    "});
 3787
 3788    cx.set_state(indoc! {"
 3789        «hElLo, WoRld!ˇ»
 3790    "});
 3791    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3792    cx.assert_editor_state(indoc! {"
 3793        «HeLlO, wOrLD!ˇ»
 3794    "});
 3795}
 3796
 3797#[gpui::test]
 3798fn test_duplicate_line(cx: &mut TestAppContext) {
 3799    init_test(cx, |_| {});
 3800
 3801    let view = cx.add_window(|cx| {
 3802        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3803        build_editor(buffer, cx)
 3804    });
 3805    _ = view.update(cx, |view, cx| {
 3806        view.change_selections(None, cx, |s| {
 3807            s.select_display_ranges([
 3808                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3809                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3810                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3811                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3812            ])
 3813        });
 3814        view.duplicate_line_down(&DuplicateLineDown, cx);
 3815        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3816        assert_eq!(
 3817            view.selections.display_ranges(cx),
 3818            vec![
 3819                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3820                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3821                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3822                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3823            ]
 3824        );
 3825    });
 3826
 3827    let view = cx.add_window(|cx| {
 3828        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3829        build_editor(buffer, cx)
 3830    });
 3831    _ = view.update(cx, |view, cx| {
 3832        view.change_selections(None, cx, |s| {
 3833            s.select_display_ranges([
 3834                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3835                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3836            ])
 3837        });
 3838        view.duplicate_line_down(&DuplicateLineDown, cx);
 3839        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3840        assert_eq!(
 3841            view.selections.display_ranges(cx),
 3842            vec![
 3843                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3844                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3845            ]
 3846        );
 3847    });
 3848
 3849    // With `move_upwards` the selections stay in place, except for
 3850    // the lines inserted above them
 3851    let view = cx.add_window(|cx| {
 3852        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3853        build_editor(buffer, cx)
 3854    });
 3855    _ = view.update(cx, |view, cx| {
 3856        view.change_selections(None, cx, |s| {
 3857            s.select_display_ranges([
 3858                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3859                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3860                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3861                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3862            ])
 3863        });
 3864        view.duplicate_line_up(&DuplicateLineUp, cx);
 3865        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3866        assert_eq!(
 3867            view.selections.display_ranges(cx),
 3868            vec![
 3869                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3870                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3871                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3872                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3873            ]
 3874        );
 3875    });
 3876
 3877    let view = cx.add_window(|cx| {
 3878        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3879        build_editor(buffer, cx)
 3880    });
 3881    _ = view.update(cx, |view, cx| {
 3882        view.change_selections(None, cx, |s| {
 3883            s.select_display_ranges([
 3884                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3885                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3886            ])
 3887        });
 3888        view.duplicate_line_up(&DuplicateLineUp, cx);
 3889        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3890        assert_eq!(
 3891            view.selections.display_ranges(cx),
 3892            vec![
 3893                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3894                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3895            ]
 3896        );
 3897    });
 3898
 3899    let view = cx.add_window(|cx| {
 3900        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3901        build_editor(buffer, cx)
 3902    });
 3903    _ = view.update(cx, |view, cx| {
 3904        view.change_selections(None, cx, |s| {
 3905            s.select_display_ranges([
 3906                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3907                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3908            ])
 3909        });
 3910        view.duplicate_selection(&DuplicateSelection, cx);
 3911        assert_eq!(view.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 3912        assert_eq!(
 3913            view.selections.display_ranges(cx),
 3914            vec![
 3915                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3916                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 3917            ]
 3918        );
 3919    });
 3920}
 3921
 3922#[gpui::test]
 3923fn test_move_line_up_down(cx: &mut TestAppContext) {
 3924    init_test(cx, |_| {});
 3925
 3926    let view = cx.add_window(|cx| {
 3927        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3928        build_editor(buffer, cx)
 3929    });
 3930    _ = view.update(cx, |view, cx| {
 3931        view.fold_creases(
 3932            vec![
 3933                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3934                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3935                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3936            ],
 3937            true,
 3938            cx,
 3939        );
 3940        view.change_selections(None, cx, |s| {
 3941            s.select_display_ranges([
 3942                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3943                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3944                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3945                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3946            ])
 3947        });
 3948        assert_eq!(
 3949            view.display_text(cx),
 3950            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3951        );
 3952
 3953        view.move_line_up(&MoveLineUp, cx);
 3954        assert_eq!(
 3955            view.display_text(cx),
 3956            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3957        );
 3958        assert_eq!(
 3959            view.selections.display_ranges(cx),
 3960            vec![
 3961                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3962                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3963                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3964                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3965            ]
 3966        );
 3967    });
 3968
 3969    _ = view.update(cx, |view, cx| {
 3970        view.move_line_down(&MoveLineDown, cx);
 3971        assert_eq!(
 3972            view.display_text(cx),
 3973            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3974        );
 3975        assert_eq!(
 3976            view.selections.display_ranges(cx),
 3977            vec![
 3978                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3979                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3980                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3981                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3982            ]
 3983        );
 3984    });
 3985
 3986    _ = view.update(cx, |view, cx| {
 3987        view.move_line_down(&MoveLineDown, cx);
 3988        assert_eq!(
 3989            view.display_text(cx),
 3990            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3991        );
 3992        assert_eq!(
 3993            view.selections.display_ranges(cx),
 3994            vec![
 3995                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3996                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3997                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3998                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3999            ]
 4000        );
 4001    });
 4002
 4003    _ = view.update(cx, |view, cx| {
 4004        view.move_line_up(&MoveLineUp, cx);
 4005        assert_eq!(
 4006            view.display_text(cx),
 4007            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4008        );
 4009        assert_eq!(
 4010            view.selections.display_ranges(cx),
 4011            vec![
 4012                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4013                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4014                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4015                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4016            ]
 4017        );
 4018    });
 4019}
 4020
 4021#[gpui::test]
 4022fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4023    init_test(cx, |_| {});
 4024
 4025    let editor = cx.add_window(|cx| {
 4026        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4027        build_editor(buffer, cx)
 4028    });
 4029    _ = editor.update(cx, |editor, cx| {
 4030        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4031        editor.insert_blocks(
 4032            [BlockProperties {
 4033                style: BlockStyle::Fixed,
 4034                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4035                height: 1,
 4036                render: Arc::new(|_| div().into_any()),
 4037                priority: 0,
 4038            }],
 4039            Some(Autoscroll::fit()),
 4040            cx,
 4041        );
 4042        editor.change_selections(None, cx, |s| {
 4043            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4044        });
 4045        editor.move_line_down(&MoveLineDown, cx);
 4046    });
 4047}
 4048
 4049#[gpui::test]
 4050async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4051    init_test(cx, |_| {});
 4052
 4053    let mut cx = EditorTestContext::new(cx).await;
 4054    cx.set_state(
 4055        &"
 4056            ˇzero
 4057            one
 4058            two
 4059            three
 4060            four
 4061            five
 4062        "
 4063        .unindent(),
 4064    );
 4065
 4066    // Create a four-line block that replaces three lines of text.
 4067    cx.update_editor(|editor, cx| {
 4068        let snapshot = editor.snapshot(cx);
 4069        let snapshot = &snapshot.buffer_snapshot;
 4070        let placement = BlockPlacement::Replace(
 4071            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4072        );
 4073        editor.insert_blocks(
 4074            [BlockProperties {
 4075                placement,
 4076                height: 4,
 4077                style: BlockStyle::Sticky,
 4078                render: Arc::new(|_| gpui::div().into_any_element()),
 4079                priority: 0,
 4080            }],
 4081            None,
 4082            cx,
 4083        );
 4084    });
 4085
 4086    // Move down so that the cursor touches the block.
 4087    cx.update_editor(|editor, cx| {
 4088        editor.move_down(&Default::default(), cx);
 4089    });
 4090    cx.assert_editor_state(
 4091        &"
 4092            zero
 4093            «one
 4094            two
 4095            threeˇ»
 4096            four
 4097            five
 4098        "
 4099        .unindent(),
 4100    );
 4101
 4102    // Move down past the block.
 4103    cx.update_editor(|editor, cx| {
 4104        editor.move_down(&Default::default(), cx);
 4105    });
 4106    cx.assert_editor_state(
 4107        &"
 4108            zero
 4109            one
 4110            two
 4111            three
 4112            ˇfour
 4113            five
 4114        "
 4115        .unindent(),
 4116    );
 4117}
 4118
 4119#[gpui::test]
 4120fn test_transpose(cx: &mut TestAppContext) {
 4121    init_test(cx, |_| {});
 4122
 4123    _ = cx.add_window(|cx| {
 4124        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4125        editor.set_style(EditorStyle::default(), cx);
 4126        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4127        editor.transpose(&Default::default(), cx);
 4128        assert_eq!(editor.text(cx), "bac");
 4129        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4130
 4131        editor.transpose(&Default::default(), cx);
 4132        assert_eq!(editor.text(cx), "bca");
 4133        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4134
 4135        editor.transpose(&Default::default(), cx);
 4136        assert_eq!(editor.text(cx), "bac");
 4137        assert_eq!(editor.selections.ranges(cx), [3..3]);
 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([3..3]));
 4146        editor.transpose(&Default::default(), cx);
 4147        assert_eq!(editor.text(cx), "acb\nde");
 4148        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4149
 4150        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4151        editor.transpose(&Default::default(), cx);
 4152        assert_eq!(editor.text(cx), "acbd\ne");
 4153        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4154
 4155        editor.transpose(&Default::default(), cx);
 4156        assert_eq!(editor.text(cx), "acbde\n");
 4157        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4158
 4159        editor.transpose(&Default::default(), cx);
 4160        assert_eq!(editor.text(cx), "acbd\ne");
 4161        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4162
 4163        editor
 4164    });
 4165
 4166    _ = cx.add_window(|cx| {
 4167        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4168        editor.set_style(EditorStyle::default(), cx);
 4169        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4170        editor.transpose(&Default::default(), cx);
 4171        assert_eq!(editor.text(cx), "bacd\ne");
 4172        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4173
 4174        editor.transpose(&Default::default(), cx);
 4175        assert_eq!(editor.text(cx), "bcade\n");
 4176        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4177
 4178        editor.transpose(&Default::default(), cx);
 4179        assert_eq!(editor.text(cx), "bcda\ne");
 4180        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4181
 4182        editor.transpose(&Default::default(), cx);
 4183        assert_eq!(editor.text(cx), "bcade\n");
 4184        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4185
 4186        editor.transpose(&Default::default(), cx);
 4187        assert_eq!(editor.text(cx), "bcaed\n");
 4188        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4189
 4190        editor
 4191    });
 4192
 4193    _ = cx.add_window(|cx| {
 4194        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4195        editor.set_style(EditorStyle::default(), cx);
 4196        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4197        editor.transpose(&Default::default(), cx);
 4198        assert_eq!(editor.text(cx), "🏀🍐✋");
 4199        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4200
 4201        editor.transpose(&Default::default(), cx);
 4202        assert_eq!(editor.text(cx), "🏀✋🍐");
 4203        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4204
 4205        editor.transpose(&Default::default(), cx);
 4206        assert_eq!(editor.text(cx), "🏀🍐✋");
 4207        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4208
 4209        editor
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214async fn test_rewrap(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let mut cx = EditorTestContext::new(cx).await;
 4218
 4219    let language_with_c_comments = Arc::new(Language::new(
 4220        LanguageConfig {
 4221            line_comments: vec!["// ".into()],
 4222            ..LanguageConfig::default()
 4223        },
 4224        None,
 4225    ));
 4226    let language_with_pound_comments = Arc::new(Language::new(
 4227        LanguageConfig {
 4228            line_comments: vec!["# ".into()],
 4229            ..LanguageConfig::default()
 4230        },
 4231        None,
 4232    ));
 4233    let markdown_language = Arc::new(Language::new(
 4234        LanguageConfig {
 4235            name: "Markdown".into(),
 4236            ..LanguageConfig::default()
 4237        },
 4238        None,
 4239    ));
 4240    let language_with_doc_comments = Arc::new(Language::new(
 4241        LanguageConfig {
 4242            line_comments: vec!["// ".into(), "/// ".into()],
 4243            ..LanguageConfig::default()
 4244        },
 4245        Some(tree_sitter_rust::LANGUAGE.into()),
 4246    ));
 4247
 4248    let plaintext_language = Arc::new(Language::new(
 4249        LanguageConfig {
 4250            name: "Plain Text".into(),
 4251            ..LanguageConfig::default()
 4252        },
 4253        None,
 4254    ));
 4255
 4256    assert_rewrap(
 4257        indoc! {"
 4258            // ˇ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.
 4259        "},
 4260        indoc! {"
 4261            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4262            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4263            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4264            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4265            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4266            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4267            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4268            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4269            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4270            // porttitor id. Aliquam id accumsan eros.
 4271        "},
 4272        language_with_c_comments.clone(),
 4273        &mut cx,
 4274    );
 4275
 4276    // Test that rewrapping works inside of a selection
 4277    assert_rewrap(
 4278        indoc! {"
 4279            «// 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.ˇ»
 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 cursors that expand to the same region are collapsed.
 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            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4303            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4304        "},
 4305        indoc! {"
 4306            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4307            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4308            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4309            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4310            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4311            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4312            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4313            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4314            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4315            // porttitor id. Aliquam id accumsan eros.
 4316        "},
 4317        language_with_c_comments.clone(),
 4318        &mut cx,
 4319    );
 4320
 4321    // Test that non-contiguous selections are treated separately.
 4322    assert_rewrap(
 4323        indoc! {"
 4324            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4325            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4326            //
 4327            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4328            // ˇ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.
 4329        "},
 4330        indoc! {"
 4331            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4332            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4333            // auctor, eu lacinia sapien scelerisque.
 4334            //
 4335            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4336            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4337            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4338            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4339            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4340            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4341            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4342        "},
 4343        language_with_c_comments.clone(),
 4344        &mut cx,
 4345    );
 4346
 4347    // Test that different comment prefixes are supported.
 4348    assert_rewrap(
 4349        indoc! {"
 4350            # ˇ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.
 4351        "},
 4352        indoc! {"
 4353            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4354            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4355            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4356            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4357            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4358            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4359            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4360            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4361            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4362            # accumsan eros.
 4363        "},
 4364        language_with_pound_comments.clone(),
 4365        &mut cx,
 4366    );
 4367
 4368    // Test that rewrapping is ignored outside of comments in most languages.
 4369    assert_rewrap(
 4370        indoc! {"
 4371            /// Adds two numbers.
 4372            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4373            fn add(a: u32, b: u32) -> u32 {
 4374                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ˇ
 4375            }
 4376        "},
 4377        indoc! {"
 4378            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4379            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4380            fn add(a: u32, b: u32) -> u32 {
 4381                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ˇ
 4382            }
 4383        "},
 4384        language_with_doc_comments.clone(),
 4385        &mut cx,
 4386    );
 4387
 4388    // Test that rewrapping works in Markdown and Plain Text languages.
 4389    assert_rewrap(
 4390        indoc! {"
 4391            # Hello
 4392
 4393            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.
 4394        "},
 4395        indoc! {"
 4396            # Hello
 4397
 4398            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4399            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4400            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4401            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4402            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4403            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4404            Integer sit amet scelerisque nisi.
 4405        "},
 4406        markdown_language,
 4407        &mut cx,
 4408    );
 4409
 4410    assert_rewrap(
 4411        indoc! {"
 4412            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.
 4413        "},
 4414        indoc! {"
 4415            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4416            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4417            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4418            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4419            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4420            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4421            Integer sit amet scelerisque nisi.
 4422        "},
 4423        plaintext_language,
 4424        &mut cx,
 4425    );
 4426
 4427    // Test rewrapping unaligned comments in a selection.
 4428    assert_rewrap(
 4429        indoc! {"
 4430            fn foo() {
 4431                if true {
 4432            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4433            // Praesent semper egestas tellus id dignissim.ˇ»
 4434                    do_something();
 4435                } else {
 4436                    //
 4437                }
 4438            }
 4439        "},
 4440        indoc! {"
 4441            fn foo() {
 4442                if true {
 4443            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4444                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4445                    // egestas tellus id dignissim.ˇ»
 4446                    do_something();
 4447                } else {
 4448                    //
 4449                }
 4450            }
 4451        "},
 4452        language_with_doc_comments.clone(),
 4453        &mut cx,
 4454    );
 4455
 4456    assert_rewrap(
 4457        indoc! {"
 4458            fn foo() {
 4459                if true {
 4460            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4461            // Praesent semper egestas tellus id dignissim.»
 4462                    do_something();
 4463                } else {
 4464                    //
 4465                }
 4466
 4467            }
 4468        "},
 4469        indoc! {"
 4470            fn foo() {
 4471                if true {
 4472            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4473                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4474                    // egestas tellus id dignissim.»
 4475                    do_something();
 4476                } else {
 4477                    //
 4478                }
 4479
 4480            }
 4481        "},
 4482        language_with_doc_comments.clone(),
 4483        &mut cx,
 4484    );
 4485
 4486    #[track_caller]
 4487    fn assert_rewrap(
 4488        unwrapped_text: &str,
 4489        wrapped_text: &str,
 4490        language: Arc<Language>,
 4491        cx: &mut EditorTestContext,
 4492    ) {
 4493        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4494        cx.set_state(unwrapped_text);
 4495        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4496        cx.assert_editor_state(wrapped_text);
 4497    }
 4498}
 4499
 4500#[gpui::test]
 4501async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4502    init_test(cx, |_| {});
 4503
 4504    let mut cx = EditorTestContext::new(cx).await;
 4505
 4506    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4507    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4508    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4509
 4510    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4511    cx.set_state("two ˇfour ˇsix ˇ");
 4512    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4513    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4514
 4515    // Paste again but with only two cursors. Since the number of cursors doesn't
 4516    // match the number of slices in the clipboard, the entire clipboard text
 4517    // is pasted at each cursor.
 4518    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4519    cx.update_editor(|e, cx| {
 4520        e.handle_input("( ", cx);
 4521        e.paste(&Paste, cx);
 4522        e.handle_input(") ", cx);
 4523    });
 4524    cx.assert_editor_state(
 4525        &([
 4526            "( one✅ ",
 4527            "three ",
 4528            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4529            "three ",
 4530            "five ) ˇ",
 4531        ]
 4532        .join("\n")),
 4533    );
 4534
 4535    // Cut with three selections, one of which is full-line.
 4536    cx.set_state(indoc! {"
 4537        1«2ˇ»3
 4538        4ˇ567
 4539        «8ˇ»9"});
 4540    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4541    cx.assert_editor_state(indoc! {"
 4542        1ˇ3
 4543        ˇ9"});
 4544
 4545    // Paste with three selections, noticing how the copied selection that was full-line
 4546    // gets inserted before the second cursor.
 4547    cx.set_state(indoc! {"
 4548        1ˇ3
 4549 4550        «oˇ»ne"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        12ˇ3
 4554        4567
 4555 4556        8ˇne"});
 4557
 4558    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4559    cx.set_state(indoc! {"
 4560        The quick brown
 4561        fox juˇmps over
 4562        the lazy dog"});
 4563    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4564    assert_eq!(
 4565        cx.read_from_clipboard()
 4566            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4567        Some("fox jumps over\n".to_string())
 4568    );
 4569
 4570    // Paste with three selections, noticing how the copied full-line selection is inserted
 4571    // before the empty selections but replaces the selection that is non-empty.
 4572    cx.set_state(indoc! {"
 4573        Tˇhe quick brown
 4574        «foˇ»x jumps over
 4575        tˇhe lazy dog"});
 4576    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4577    cx.assert_editor_state(indoc! {"
 4578        fox jumps over
 4579        Tˇhe quick brown
 4580        fox jumps over
 4581        ˇx jumps over
 4582        fox jumps over
 4583        tˇhe lazy dog"});
 4584}
 4585
 4586#[gpui::test]
 4587async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4588    init_test(cx, |_| {});
 4589
 4590    let mut cx = EditorTestContext::new(cx).await;
 4591    let language = Arc::new(Language::new(
 4592        LanguageConfig::default(),
 4593        Some(tree_sitter_rust::LANGUAGE.into()),
 4594    ));
 4595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4596
 4597    // Cut an indented block, without the leading whitespace.
 4598    cx.set_state(indoc! {"
 4599        const a: B = (
 4600            c(),
 4601            «d(
 4602                e,
 4603                f
 4604            )ˇ»
 4605        );
 4606    "});
 4607    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4608    cx.assert_editor_state(indoc! {"
 4609        const a: B = (
 4610            c(),
 4611            ˇ
 4612        );
 4613    "});
 4614
 4615    // Paste it at the same position.
 4616    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4617    cx.assert_editor_state(indoc! {"
 4618        const a: B = (
 4619            c(),
 4620            d(
 4621                e,
 4622                f
 4623 4624        );
 4625    "});
 4626
 4627    // Paste it at a line with a lower indent level.
 4628    cx.set_state(indoc! {"
 4629        ˇ
 4630        const a: B = (
 4631            c(),
 4632        );
 4633    "});
 4634    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4635    cx.assert_editor_state(indoc! {"
 4636        d(
 4637            e,
 4638            f
 4639 4640        const a: B = (
 4641            c(),
 4642        );
 4643    "});
 4644
 4645    // Cut an indented block, with the leading whitespace.
 4646    cx.set_state(indoc! {"
 4647        const a: B = (
 4648            c(),
 4649        «    d(
 4650                e,
 4651                f
 4652            )
 4653        ˇ»);
 4654    "});
 4655    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4656    cx.assert_editor_state(indoc! {"
 4657        const a: B = (
 4658            c(),
 4659        ˇ);
 4660    "});
 4661
 4662    // Paste it at the same position.
 4663    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        const a: B = (
 4666            c(),
 4667            d(
 4668                e,
 4669                f
 4670            )
 4671        ˇ);
 4672    "});
 4673
 4674    // Paste it at a line with a higher indent level.
 4675    cx.set_state(indoc! {"
 4676        const a: B = (
 4677            c(),
 4678            d(
 4679                e,
 4680 4681            )
 4682        );
 4683    "});
 4684    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4685    cx.assert_editor_state(indoc! {"
 4686        const a: B = (
 4687            c(),
 4688            d(
 4689                e,
 4690                f    d(
 4691                    e,
 4692                    f
 4693                )
 4694        ˇ
 4695            )
 4696        );
 4697    "});
 4698}
 4699
 4700#[gpui::test]
 4701fn test_select_all(cx: &mut TestAppContext) {
 4702    init_test(cx, |_| {});
 4703
 4704    let view = cx.add_window(|cx| {
 4705        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4706        build_editor(buffer, cx)
 4707    });
 4708    _ = view.update(cx, |view, cx| {
 4709        view.select_all(&SelectAll, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4713        );
 4714    });
 4715}
 4716
 4717#[gpui::test]
 4718fn test_select_line(cx: &mut TestAppContext) {
 4719    init_test(cx, |_| {});
 4720
 4721    let view = cx.add_window(|cx| {
 4722        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4723        build_editor(buffer, cx)
 4724    });
 4725    _ = view.update(cx, |view, cx| {
 4726        view.change_selections(None, cx, |s| {
 4727            s.select_display_ranges([
 4728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4730                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4731                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4732            ])
 4733        });
 4734        view.select_line(&SelectLine, cx);
 4735        assert_eq!(
 4736            view.selections.display_ranges(cx),
 4737            vec![
 4738                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4739                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4740            ]
 4741        );
 4742    });
 4743
 4744    _ = view.update(cx, |view, cx| {
 4745        view.select_line(&SelectLine, cx);
 4746        assert_eq!(
 4747            view.selections.display_ranges(cx),
 4748            vec![
 4749                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4750                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4751            ]
 4752        );
 4753    });
 4754
 4755    _ = view.update(cx, |view, cx| {
 4756        view.select_line(&SelectLine, cx);
 4757        assert_eq!(
 4758            view.selections.display_ranges(cx),
 4759            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4760        );
 4761    });
 4762}
 4763
 4764#[gpui::test]
 4765fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4766    init_test(cx, |_| {});
 4767
 4768    let view = cx.add_window(|cx| {
 4769        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4770        build_editor(buffer, cx)
 4771    });
 4772    _ = view.update(cx, |view, cx| {
 4773        view.fold_creases(
 4774            vec![
 4775                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4776                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4777                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4778            ],
 4779            true,
 4780            cx,
 4781        );
 4782        view.change_selections(None, cx, |s| {
 4783            s.select_display_ranges([
 4784                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4785                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4786                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4787                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4788            ])
 4789        });
 4790        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4791    });
 4792
 4793    _ = view.update(cx, |view, cx| {
 4794        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4795        assert_eq!(
 4796            view.display_text(cx),
 4797            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4798        );
 4799        assert_eq!(
 4800            view.selections.display_ranges(cx),
 4801            [
 4802                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4803                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4804                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4805                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4806            ]
 4807        );
 4808    });
 4809
 4810    _ = view.update(cx, |view, cx| {
 4811        view.change_selections(None, cx, |s| {
 4812            s.select_display_ranges([
 4813                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4814            ])
 4815        });
 4816        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4817        assert_eq!(
 4818            view.display_text(cx),
 4819            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4820        );
 4821        assert_eq!(
 4822            view.selections.display_ranges(cx),
 4823            [
 4824                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4825                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4826                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4827                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4828                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4829                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4830                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4831                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4832            ]
 4833        );
 4834    });
 4835}
 4836
 4837#[gpui::test]
 4838async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4839    init_test(cx, |_| {});
 4840
 4841    let mut cx = EditorTestContext::new(cx).await;
 4842
 4843    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4844    cx.set_state(indoc!(
 4845        r#"abc
 4846           defˇghi
 4847
 4848           jk
 4849           nlmo
 4850           "#
 4851    ));
 4852
 4853    cx.update_editor(|editor, cx| {
 4854        editor.add_selection_above(&Default::default(), cx);
 4855    });
 4856
 4857    cx.assert_editor_state(indoc!(
 4858        r#"abcˇ
 4859           defˇghi
 4860
 4861           jk
 4862           nlmo
 4863           "#
 4864    ));
 4865
 4866    cx.update_editor(|editor, cx| {
 4867        editor.add_selection_above(&Default::default(), cx);
 4868    });
 4869
 4870    cx.assert_editor_state(indoc!(
 4871        r#"abcˇ
 4872            defˇghi
 4873
 4874            jk
 4875            nlmo
 4876            "#
 4877    ));
 4878
 4879    cx.update_editor(|view, cx| {
 4880        view.add_selection_below(&Default::default(), cx);
 4881    });
 4882
 4883    cx.assert_editor_state(indoc!(
 4884        r#"abc
 4885           defˇghi
 4886
 4887           jk
 4888           nlmo
 4889           "#
 4890    ));
 4891
 4892    cx.update_editor(|view, cx| {
 4893        view.undo_selection(&Default::default(), cx);
 4894    });
 4895
 4896    cx.assert_editor_state(indoc!(
 4897        r#"abcˇ
 4898           defˇghi
 4899
 4900           jk
 4901           nlmo
 4902           "#
 4903    ));
 4904
 4905    cx.update_editor(|view, cx| {
 4906        view.redo_selection(&Default::default(), cx);
 4907    });
 4908
 4909    cx.assert_editor_state(indoc!(
 4910        r#"abc
 4911           defˇghi
 4912
 4913           jk
 4914           nlmo
 4915           "#
 4916    ));
 4917
 4918    cx.update_editor(|view, cx| {
 4919        view.add_selection_below(&Default::default(), cx);
 4920    });
 4921
 4922    cx.assert_editor_state(indoc!(
 4923        r#"abc
 4924           defˇghi
 4925
 4926           jk
 4927           nlmˇo
 4928           "#
 4929    ));
 4930
 4931    cx.update_editor(|view, cx| {
 4932        view.add_selection_below(&Default::default(), cx);
 4933    });
 4934
 4935    cx.assert_editor_state(indoc!(
 4936        r#"abc
 4937           defˇghi
 4938
 4939           jk
 4940           nlmˇo
 4941           "#
 4942    ));
 4943
 4944    // change selections
 4945    cx.set_state(indoc!(
 4946        r#"abc
 4947           def«ˇg»hi
 4948
 4949           jk
 4950           nlmo
 4951           "#
 4952    ));
 4953
 4954    cx.update_editor(|view, cx| {
 4955        view.add_selection_below(&Default::default(), cx);
 4956    });
 4957
 4958    cx.assert_editor_state(indoc!(
 4959        r#"abc
 4960           def«ˇg»hi
 4961
 4962           jk
 4963           nlm«ˇo»
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|view, cx| {
 4968        view.add_selection_below(&Default::default(), cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abc
 4973           def«ˇg»hi
 4974
 4975           jk
 4976           nlm«ˇo»
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|view, cx| {
 4981        view.add_selection_above(&Default::default(), cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abc
 4986           def«ˇg»hi
 4987
 4988           jk
 4989           nlmo
 4990           "#
 4991    ));
 4992
 4993    cx.update_editor(|view, cx| {
 4994        view.add_selection_above(&Default::default(), cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"abc
 4999           def«ˇg»hi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    // Change selections again
 5007    cx.set_state(indoc!(
 5008        r#"a«bc
 5009           defgˇ»hi
 5010
 5011           jk
 5012           nlmo
 5013           "#
 5014    ));
 5015
 5016    cx.update_editor(|view, cx| {
 5017        view.add_selection_below(&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    cx.update_editor(|view, cx| {
 5030        view.add_selection_below(&Default::default(), cx);
 5031    });
 5032    cx.assert_editor_state(indoc!(
 5033        r#"a«bcˇ»
 5034           d«efgˇ»hi
 5035
 5036           j«kˇ»
 5037           n«lmoˇ»
 5038           "#
 5039    ));
 5040    cx.update_editor(|view, cx| {
 5041        view.add_selection_above(&Default::default(), cx);
 5042    });
 5043
 5044    cx.assert_editor_state(indoc!(
 5045        r#"a«bcˇ»
 5046           d«efgˇ»hi
 5047
 5048           j«kˇ»
 5049           nlmo
 5050           "#
 5051    ));
 5052
 5053    // Change selections again
 5054    cx.set_state(indoc!(
 5055        r#"abc
 5056           d«ˇefghi
 5057
 5058           jk
 5059           nlm»o
 5060           "#
 5061    ));
 5062
 5063    cx.update_editor(|view, cx| {
 5064        view.add_selection_above(&Default::default(), cx);
 5065    });
 5066
 5067    cx.assert_editor_state(indoc!(
 5068        r#"a«ˇbc»
 5069           d«ˇef»ghi
 5070
 5071           j«ˇk»
 5072           n«ˇlm»o
 5073           "#
 5074    ));
 5075
 5076    cx.update_editor(|view, cx| {
 5077        view.add_selection_below(&Default::default(), cx);
 5078    });
 5079
 5080    cx.assert_editor_state(indoc!(
 5081        r#"abc
 5082           d«ˇef»ghi
 5083
 5084           j«ˇk»
 5085           n«ˇlm»o
 5086           "#
 5087    ));
 5088}
 5089
 5090#[gpui::test]
 5091async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5092    init_test(cx, |_| {});
 5093
 5094    let mut cx = EditorTestContext::new(cx).await;
 5095    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5096
 5097    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5098        .unwrap();
 5099    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5104
 5105    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5106    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5107
 5108    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5109    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5110
 5111    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5112        .unwrap();
 5113    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5114
 5115    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5118}
 5119
 5120#[gpui::test]
 5121async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5122    init_test(cx, |_| {});
 5123
 5124    let mut cx = EditorTestContext::new(cx).await;
 5125    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5126
 5127    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5128        .unwrap();
 5129    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5130}
 5131
 5132#[gpui::test]
 5133async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5134    init_test(cx, |_| {});
 5135
 5136    let mut cx = EditorTestContext::new(cx).await;
 5137    cx.set_state(
 5138        r#"let foo = 2;
 5139lˇet foo = 2;
 5140let fooˇ = 2;
 5141let foo = 2;
 5142let foo = ˇ2;"#,
 5143    );
 5144
 5145    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5146        .unwrap();
 5147    cx.assert_editor_state(
 5148        r#"let foo = 2;
 5149«letˇ» foo = 2;
 5150let «fooˇ» = 2;
 5151let foo = 2;
 5152let foo = «2ˇ»;"#,
 5153    );
 5154
 5155    // noop for multiple selections with different contents
 5156    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5157        .unwrap();
 5158    cx.assert_editor_state(
 5159        r#"let foo = 2;
 5160«letˇ» foo = 2;
 5161let «fooˇ» = 2;
 5162let foo = 2;
 5163let foo = «2ˇ»;"#,
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let mut cx =
 5172        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5173
 5174    cx.assert_editor_state(indoc! {"
 5175        ˇbbb
 5176        ccc
 5177
 5178        bbb
 5179        ccc
 5180        "});
 5181    cx.dispatch_action(SelectPrevious::default());
 5182    cx.assert_editor_state(indoc! {"
 5183                «bbbˇ»
 5184                ccc
 5185
 5186                bbb
 5187                ccc
 5188                "});
 5189    cx.dispatch_action(SelectPrevious::default());
 5190    cx.assert_editor_state(indoc! {"
 5191                «bbbˇ»
 5192                ccc
 5193
 5194                «bbbˇ»
 5195                ccc
 5196                "});
 5197}
 5198
 5199#[gpui::test]
 5200async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5201    init_test(cx, |_| {});
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5205
 5206    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5207        .unwrap();
 5208    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5209
 5210    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5211        .unwrap();
 5212    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5213
 5214    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5215    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5216
 5217    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5218    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5219
 5220    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5221        .unwrap();
 5222    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5223
 5224    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5225        .unwrap();
 5226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5227
 5228    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5229        .unwrap();
 5230    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5231}
 5232
 5233#[gpui::test]
 5234async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5235    init_test(cx, |_| {});
 5236
 5237    let mut cx = EditorTestContext::new(cx).await;
 5238    cx.set_state(
 5239        r#"let foo = 2;
 5240lˇet foo = 2;
 5241let fooˇ = 2;
 5242let foo = 2;
 5243let foo = ˇ2;"#,
 5244    );
 5245
 5246    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5247        .unwrap();
 5248    cx.assert_editor_state(
 5249        r#"let foo = 2;
 5250«letˇ» foo = 2;
 5251let «fooˇ» = 2;
 5252let foo = 2;
 5253let foo = «2ˇ»;"#,
 5254    );
 5255
 5256    // noop for multiple selections with different contents
 5257    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5258        .unwrap();
 5259    cx.assert_editor_state(
 5260        r#"let foo = 2;
 5261«letˇ» foo = 2;
 5262let «fooˇ» = 2;
 5263let foo = 2;
 5264let foo = «2ˇ»;"#,
 5265    );
 5266}
 5267
 5268#[gpui::test]
 5269async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5270    init_test(cx, |_| {});
 5271
 5272    let mut cx = EditorTestContext::new(cx).await;
 5273    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5274
 5275    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5276        .unwrap();
 5277    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5278
 5279    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5280        .unwrap();
 5281    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5282
 5283    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5284    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5285
 5286    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5287    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5288
 5289    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5290        .unwrap();
 5291    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5292
 5293    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5294        .unwrap();
 5295    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5296}
 5297
 5298#[gpui::test]
 5299async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5300    init_test(cx, |_| {});
 5301
 5302    let language = Arc::new(Language::new(
 5303        LanguageConfig::default(),
 5304        Some(tree_sitter_rust::LANGUAGE.into()),
 5305    ));
 5306
 5307    let text = r#"
 5308        use mod1::mod2::{mod3, mod4};
 5309
 5310        fn fn_1(param1: bool, param2: &str) {
 5311            let var1 = "text";
 5312        }
 5313    "#
 5314    .unindent();
 5315
 5316    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5317    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5318    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5319
 5320    editor
 5321        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5322        .await;
 5323
 5324    editor.update(cx, |view, cx| {
 5325        view.change_selections(None, cx, |s| {
 5326            s.select_display_ranges([
 5327                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5328                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5329                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5330            ]);
 5331        });
 5332        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5333    });
 5334    editor.update(cx, |editor, cx| {
 5335        assert_text_with_selections(
 5336            editor,
 5337            indoc! {r#"
 5338                use mod1::mod2::{mod3, «mod4ˇ»};
 5339
 5340                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5341                    let var1 = "«textˇ»";
 5342                }
 5343            "#},
 5344            cx,
 5345        );
 5346    });
 5347
 5348    editor.update(cx, |view, cx| {
 5349        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5350    });
 5351    editor.update(cx, |editor, cx| {
 5352        assert_text_with_selections(
 5353            editor,
 5354            indoc! {r#"
 5355                use mod1::mod2::«{mod3, mod4}ˇ»;
 5356
 5357                «ˇfn fn_1(param1: bool, param2: &str) {
 5358                    let var1 = "text";
 5359 5360            "#},
 5361            cx,
 5362        );
 5363    });
 5364
 5365    editor.update(cx, |view, cx| {
 5366        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5367    });
 5368    assert_eq!(
 5369        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5370        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5371    );
 5372
 5373    // Trying to expand the selected syntax node one more time has no effect.
 5374    editor.update(cx, |view, cx| {
 5375        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5376    });
 5377    assert_eq!(
 5378        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5379        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5380    );
 5381
 5382    editor.update(cx, |view, cx| {
 5383        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5384    });
 5385    editor.update(cx, |editor, cx| {
 5386        assert_text_with_selections(
 5387            editor,
 5388            indoc! {r#"
 5389                use mod1::mod2::«{mod3, mod4}ˇ»;
 5390
 5391                «ˇfn fn_1(param1: bool, param2: &str) {
 5392                    let var1 = "text";
 5393 5394            "#},
 5395            cx,
 5396        );
 5397    });
 5398
 5399    editor.update(cx, |view, cx| {
 5400        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5401    });
 5402    editor.update(cx, |editor, cx| {
 5403        assert_text_with_selections(
 5404            editor,
 5405            indoc! {r#"
 5406                use mod1::mod2::{mod3, «mod4ˇ»};
 5407
 5408                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5409                    let var1 = "«textˇ»";
 5410                }
 5411            "#},
 5412            cx,
 5413        );
 5414    });
 5415
 5416    editor.update(cx, |view, cx| {
 5417        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5418    });
 5419    editor.update(cx, |editor, cx| {
 5420        assert_text_with_selections(
 5421            editor,
 5422            indoc! {r#"
 5423                use mod1::mod2::{mod3, mo«ˇ»d4};
 5424
 5425                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5426                    let var1 = "te«ˇ»xt";
 5427                }
 5428            "#},
 5429            cx,
 5430        );
 5431    });
 5432
 5433    // Trying to shrink the selected syntax node one more time has no effect.
 5434    editor.update(cx, |view, cx| {
 5435        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5436    });
 5437    editor.update(cx, |editor, cx| {
 5438        assert_text_with_selections(
 5439            editor,
 5440            indoc! {r#"
 5441                use mod1::mod2::{mod3, mo«ˇ»d4};
 5442
 5443                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5444                    let var1 = "te«ˇ»xt";
 5445                }
 5446            "#},
 5447            cx,
 5448        );
 5449    });
 5450
 5451    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5452    // a fold.
 5453    editor.update(cx, |view, cx| {
 5454        view.fold_creases(
 5455            vec![
 5456                Crease::simple(
 5457                    Point::new(0, 21)..Point::new(0, 24),
 5458                    FoldPlaceholder::test(),
 5459                ),
 5460                Crease::simple(
 5461                    Point::new(3, 20)..Point::new(3, 22),
 5462                    FoldPlaceholder::test(),
 5463                ),
 5464            ],
 5465            true,
 5466            cx,
 5467        );
 5468        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5469    });
 5470    editor.update(cx, |editor, cx| {
 5471        assert_text_with_selections(
 5472            editor,
 5473            indoc! {r#"
 5474                use mod1::mod2::«{mod3, mod4}ˇ»;
 5475
 5476                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5477                    «let var1 = "text";ˇ»
 5478                }
 5479            "#},
 5480            cx,
 5481        );
 5482    });
 5483}
 5484
 5485#[gpui::test]
 5486async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5487    init_test(cx, |_| {});
 5488
 5489    let language = Arc::new(
 5490        Language::new(
 5491            LanguageConfig {
 5492                brackets: BracketPairConfig {
 5493                    pairs: vec![
 5494                        BracketPair {
 5495                            start: "{".to_string(),
 5496                            end: "}".to_string(),
 5497                            close: false,
 5498                            surround: false,
 5499                            newline: true,
 5500                        },
 5501                        BracketPair {
 5502                            start: "(".to_string(),
 5503                            end: ")".to_string(),
 5504                            close: false,
 5505                            surround: false,
 5506                            newline: true,
 5507                        },
 5508                    ],
 5509                    ..Default::default()
 5510                },
 5511                ..Default::default()
 5512            },
 5513            Some(tree_sitter_rust::LANGUAGE.into()),
 5514        )
 5515        .with_indents_query(
 5516            r#"
 5517                (_ "(" ")" @end) @indent
 5518                (_ "{" "}" @end) @indent
 5519            "#,
 5520        )
 5521        .unwrap(),
 5522    );
 5523
 5524    let text = "fn a() {}";
 5525
 5526    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5527    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5528    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5529    editor
 5530        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5531        .await;
 5532
 5533    editor.update(cx, |editor, cx| {
 5534        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5535        editor.newline(&Newline, cx);
 5536        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5537        assert_eq!(
 5538            editor.selections.ranges(cx),
 5539            &[
 5540                Point::new(1, 4)..Point::new(1, 4),
 5541                Point::new(3, 4)..Point::new(3, 4),
 5542                Point::new(5, 0)..Point::new(5, 0)
 5543            ]
 5544        );
 5545    });
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    {
 5553        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5554        cx.set_state(indoc! {"
 5555            impl A {
 5556
 5557                fn b() {}
 5558
 5559            «fn c() {
 5560
 5561            }ˇ»
 5562            }
 5563        "});
 5564
 5565        cx.update_editor(|editor, cx| {
 5566            editor.autoindent(&Default::default(), cx);
 5567        });
 5568
 5569        cx.assert_editor_state(indoc! {"
 5570            impl A {
 5571
 5572                fn b() {}
 5573
 5574                «fn c() {
 5575
 5576                }ˇ»
 5577            }
 5578        "});
 5579    }
 5580
 5581    {
 5582        let mut cx = EditorTestContext::new_multibuffer(
 5583            cx,
 5584            [indoc! { "
 5585                impl A {
 5586                «
 5587                // a
 5588                fn b(){}
 5589                »
 5590                «
 5591                    }
 5592                    fn c(){}
 5593                »
 5594            "}],
 5595        );
 5596
 5597        let buffer = cx.update_editor(|editor, cx| {
 5598            let buffer = editor.buffer().update(cx, |buffer, _| {
 5599                buffer.all_buffers().iter().next().unwrap().clone()
 5600            });
 5601            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5602            buffer
 5603        });
 5604
 5605        cx.run_until_parked();
 5606        cx.update_editor(|editor, cx| {
 5607            editor.select_all(&Default::default(), cx);
 5608            editor.autoindent(&Default::default(), cx)
 5609        });
 5610        cx.run_until_parked();
 5611
 5612        cx.update(|cx| {
 5613            pretty_assertions::assert_eq!(
 5614                buffer.read(cx).text(),
 5615                indoc! { "
 5616                    impl A {
 5617
 5618                        // a
 5619                        fn b(){}
 5620
 5621
 5622                    }
 5623                    fn c(){}
 5624
 5625                " }
 5626            )
 5627        });
 5628    }
 5629}
 5630
 5631#[gpui::test]
 5632async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5633    init_test(cx, |_| {});
 5634
 5635    let mut cx = EditorTestContext::new(cx).await;
 5636
 5637    let language = Arc::new(Language::new(
 5638        LanguageConfig {
 5639            brackets: BracketPairConfig {
 5640                pairs: vec![
 5641                    BracketPair {
 5642                        start: "{".to_string(),
 5643                        end: "}".to_string(),
 5644                        close: true,
 5645                        surround: true,
 5646                        newline: true,
 5647                    },
 5648                    BracketPair {
 5649                        start: "(".to_string(),
 5650                        end: ")".to_string(),
 5651                        close: true,
 5652                        surround: true,
 5653                        newline: true,
 5654                    },
 5655                    BracketPair {
 5656                        start: "/*".to_string(),
 5657                        end: " */".to_string(),
 5658                        close: true,
 5659                        surround: true,
 5660                        newline: true,
 5661                    },
 5662                    BracketPair {
 5663                        start: "[".to_string(),
 5664                        end: "]".to_string(),
 5665                        close: false,
 5666                        surround: false,
 5667                        newline: true,
 5668                    },
 5669                    BracketPair {
 5670                        start: "\"".to_string(),
 5671                        end: "\"".to_string(),
 5672                        close: true,
 5673                        surround: true,
 5674                        newline: false,
 5675                    },
 5676                    BracketPair {
 5677                        start: "<".to_string(),
 5678                        end: ">".to_string(),
 5679                        close: false,
 5680                        surround: true,
 5681                        newline: true,
 5682                    },
 5683                ],
 5684                ..Default::default()
 5685            },
 5686            autoclose_before: "})]".to_string(),
 5687            ..Default::default()
 5688        },
 5689        Some(tree_sitter_rust::LANGUAGE.into()),
 5690    ));
 5691
 5692    cx.language_registry().add(language.clone());
 5693    cx.update_buffer(|buffer, cx| {
 5694        buffer.set_language(Some(language), cx);
 5695    });
 5696
 5697    cx.set_state(
 5698        &r#"
 5699            🏀ˇ
 5700            εˇ
 5701            ❤️ˇ
 5702        "#
 5703        .unindent(),
 5704    );
 5705
 5706    // autoclose multiple nested brackets at multiple cursors
 5707    cx.update_editor(|view, cx| {
 5708        view.handle_input("{", cx);
 5709        view.handle_input("{", cx);
 5710        view.handle_input("{", cx);
 5711    });
 5712    cx.assert_editor_state(
 5713        &"
 5714            🏀{{{ˇ}}}
 5715            ε{{{ˇ}}}
 5716            ❤️{{{ˇ}}}
 5717        "
 5718        .unindent(),
 5719    );
 5720
 5721    // insert a different closing bracket
 5722    cx.update_editor(|view, cx| {
 5723        view.handle_input(")", cx);
 5724    });
 5725    cx.assert_editor_state(
 5726        &"
 5727            🏀{{{)ˇ}}}
 5728            ε{{{)ˇ}}}
 5729            ❤️{{{)ˇ}}}
 5730        "
 5731        .unindent(),
 5732    );
 5733
 5734    // skip over the auto-closed brackets when typing a closing bracket
 5735    cx.update_editor(|view, cx| {
 5736        view.move_right(&MoveRight, cx);
 5737        view.handle_input("}", cx);
 5738        view.handle_input("}", cx);
 5739        view.handle_input("}", cx);
 5740    });
 5741    cx.assert_editor_state(
 5742        &"
 5743            🏀{{{)}}}}ˇ
 5744            ε{{{)}}}}ˇ
 5745            ❤️{{{)}}}}ˇ
 5746        "
 5747        .unindent(),
 5748    );
 5749
 5750    // autoclose multi-character pairs
 5751    cx.set_state(
 5752        &"
 5753            ˇ
 5754            ˇ
 5755        "
 5756        .unindent(),
 5757    );
 5758    cx.update_editor(|view, cx| {
 5759        view.handle_input("/", cx);
 5760        view.handle_input("*", cx);
 5761    });
 5762    cx.assert_editor_state(
 5763        &"
 5764            /*ˇ */
 5765            /*ˇ */
 5766        "
 5767        .unindent(),
 5768    );
 5769
 5770    // one cursor autocloses a multi-character pair, one cursor
 5771    // does not autoclose.
 5772    cx.set_state(
 5773        &"
 5774 5775            ˇ
 5776        "
 5777        .unindent(),
 5778    );
 5779    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5780    cx.assert_editor_state(
 5781        &"
 5782            /*ˇ */
 5783 5784        "
 5785        .unindent(),
 5786    );
 5787
 5788    // Don't autoclose if the next character isn't whitespace and isn't
 5789    // listed in the language's "autoclose_before" section.
 5790    cx.set_state("ˇa b");
 5791    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5792    cx.assert_editor_state("{ˇa b");
 5793
 5794    // Don't autoclose if `close` is false for the bracket pair
 5795    cx.set_state("ˇ");
 5796    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5797    cx.assert_editor_state("");
 5798
 5799    // Surround with brackets if text is selected
 5800    cx.set_state("«aˇ» b");
 5801    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5802    cx.assert_editor_state("{«aˇ»} b");
 5803
 5804    // Autclose pair where the start and end characters are the same
 5805    cx.set_state("");
 5806    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5807    cx.assert_editor_state("a\"ˇ\"");
 5808    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5809    cx.assert_editor_state("a\"\"ˇ");
 5810
 5811    // Don't autoclose pair if autoclose is disabled
 5812    cx.set_state("ˇ");
 5813    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5814    cx.assert_editor_state("");
 5815
 5816    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5817    cx.set_state("«aˇ» b");
 5818    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5819    cx.assert_editor_state("<«aˇ»> b");
 5820}
 5821
 5822#[gpui::test]
 5823async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5824    init_test(cx, |settings| {
 5825        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5826    });
 5827
 5828    let mut cx = EditorTestContext::new(cx).await;
 5829
 5830    let language = Arc::new(Language::new(
 5831        LanguageConfig {
 5832            brackets: BracketPairConfig {
 5833                pairs: vec![
 5834                    BracketPair {
 5835                        start: "{".to_string(),
 5836                        end: "}".to_string(),
 5837                        close: true,
 5838                        surround: true,
 5839                        newline: true,
 5840                    },
 5841                    BracketPair {
 5842                        start: "(".to_string(),
 5843                        end: ")".to_string(),
 5844                        close: true,
 5845                        surround: true,
 5846                        newline: true,
 5847                    },
 5848                    BracketPair {
 5849                        start: "[".to_string(),
 5850                        end: "]".to_string(),
 5851                        close: false,
 5852                        surround: false,
 5853                        newline: true,
 5854                    },
 5855                ],
 5856                ..Default::default()
 5857            },
 5858            autoclose_before: "})]".to_string(),
 5859            ..Default::default()
 5860        },
 5861        Some(tree_sitter_rust::LANGUAGE.into()),
 5862    ));
 5863
 5864    cx.language_registry().add(language.clone());
 5865    cx.update_buffer(|buffer, cx| {
 5866        buffer.set_language(Some(language), cx);
 5867    });
 5868
 5869    cx.set_state(
 5870        &"
 5871            ˇ
 5872            ˇ
 5873            ˇ
 5874        "
 5875        .unindent(),
 5876    );
 5877
 5878    // ensure only matching closing brackets are skipped over
 5879    cx.update_editor(|view, cx| {
 5880        view.handle_input("}", cx);
 5881        view.move_left(&MoveLeft, cx);
 5882        view.handle_input(")", cx);
 5883        view.move_left(&MoveLeft, cx);
 5884    });
 5885    cx.assert_editor_state(
 5886        &"
 5887            ˇ)}
 5888            ˇ)}
 5889            ˇ)}
 5890        "
 5891        .unindent(),
 5892    );
 5893
 5894    // skip-over closing brackets at multiple cursors
 5895    cx.update_editor(|view, cx| {
 5896        view.handle_input(")", cx);
 5897        view.handle_input("}", cx);
 5898    });
 5899    cx.assert_editor_state(
 5900        &"
 5901            )}ˇ
 5902            )}ˇ
 5903            )}ˇ
 5904        "
 5905        .unindent(),
 5906    );
 5907
 5908    // ignore non-close brackets
 5909    cx.update_editor(|view, cx| {
 5910        view.handle_input("]", cx);
 5911        view.move_left(&MoveLeft, cx);
 5912        view.handle_input("]", cx);
 5913    });
 5914    cx.assert_editor_state(
 5915        &"
 5916            )}]ˇ]
 5917            )}]ˇ]
 5918            )}]ˇ]
 5919        "
 5920        .unindent(),
 5921    );
 5922}
 5923
 5924#[gpui::test]
 5925async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5926    init_test(cx, |_| {});
 5927
 5928    let mut cx = EditorTestContext::new(cx).await;
 5929
 5930    let html_language = Arc::new(
 5931        Language::new(
 5932            LanguageConfig {
 5933                name: "HTML".into(),
 5934                brackets: BracketPairConfig {
 5935                    pairs: vec![
 5936                        BracketPair {
 5937                            start: "<".into(),
 5938                            end: ">".into(),
 5939                            close: true,
 5940                            ..Default::default()
 5941                        },
 5942                        BracketPair {
 5943                            start: "{".into(),
 5944                            end: "}".into(),
 5945                            close: true,
 5946                            ..Default::default()
 5947                        },
 5948                        BracketPair {
 5949                            start: "(".into(),
 5950                            end: ")".into(),
 5951                            close: true,
 5952                            ..Default::default()
 5953                        },
 5954                    ],
 5955                    ..Default::default()
 5956                },
 5957                autoclose_before: "})]>".into(),
 5958                ..Default::default()
 5959            },
 5960            Some(tree_sitter_html::language()),
 5961        )
 5962        .with_injection_query(
 5963            r#"
 5964            (script_element
 5965                (raw_text) @content
 5966                (#set! "language" "javascript"))
 5967            "#,
 5968        )
 5969        .unwrap(),
 5970    );
 5971
 5972    let javascript_language = Arc::new(Language::new(
 5973        LanguageConfig {
 5974            name: "JavaScript".into(),
 5975            brackets: BracketPairConfig {
 5976                pairs: vec![
 5977                    BracketPair {
 5978                        start: "/*".into(),
 5979                        end: " */".into(),
 5980                        close: true,
 5981                        ..Default::default()
 5982                    },
 5983                    BracketPair {
 5984                        start: "{".into(),
 5985                        end: "}".into(),
 5986                        close: true,
 5987                        ..Default::default()
 5988                    },
 5989                    BracketPair {
 5990                        start: "(".into(),
 5991                        end: ")".into(),
 5992                        close: true,
 5993                        ..Default::default()
 5994                    },
 5995                ],
 5996                ..Default::default()
 5997            },
 5998            autoclose_before: "})]>".into(),
 5999            ..Default::default()
 6000        },
 6001        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6002    ));
 6003
 6004    cx.language_registry().add(html_language.clone());
 6005    cx.language_registry().add(javascript_language.clone());
 6006
 6007    cx.update_buffer(|buffer, cx| {
 6008        buffer.set_language(Some(html_language), cx);
 6009    });
 6010
 6011    cx.set_state(
 6012        &r#"
 6013            <body>ˇ
 6014                <script>
 6015                    var x = 1;ˇ
 6016                </script>
 6017            </body>ˇ
 6018        "#
 6019        .unindent(),
 6020    );
 6021
 6022    // Precondition: different languages are active at different locations.
 6023    cx.update_editor(|editor, cx| {
 6024        let snapshot = editor.snapshot(cx);
 6025        let cursors = editor.selections.ranges::<usize>(cx);
 6026        let languages = cursors
 6027            .iter()
 6028            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6029            .collect::<Vec<_>>();
 6030        assert_eq!(
 6031            languages,
 6032            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6033        );
 6034    });
 6035
 6036    // Angle brackets autoclose in HTML, but not JavaScript.
 6037    cx.update_editor(|editor, cx| {
 6038        editor.handle_input("<", cx);
 6039        editor.handle_input("a", cx);
 6040    });
 6041    cx.assert_editor_state(
 6042        &r#"
 6043            <body><aˇ>
 6044                <script>
 6045                    var x = 1;<aˇ
 6046                </script>
 6047            </body><aˇ>
 6048        "#
 6049        .unindent(),
 6050    );
 6051
 6052    // Curly braces and parens autoclose in both HTML and JavaScript.
 6053    cx.update_editor(|editor, cx| {
 6054        editor.handle_input(" b=", cx);
 6055        editor.handle_input("{", cx);
 6056        editor.handle_input("c", cx);
 6057        editor.handle_input("(", cx);
 6058    });
 6059    cx.assert_editor_state(
 6060        &r#"
 6061            <body><a b={c(ˇ)}>
 6062                <script>
 6063                    var x = 1;<a b={c(ˇ)}
 6064                </script>
 6065            </body><a b={c(ˇ)}>
 6066        "#
 6067        .unindent(),
 6068    );
 6069
 6070    // Brackets that were already autoclosed are skipped.
 6071    cx.update_editor(|editor, cx| {
 6072        editor.handle_input(")", cx);
 6073        editor.handle_input("d", cx);
 6074        editor.handle_input("}", cx);
 6075    });
 6076    cx.assert_editor_state(
 6077        &r#"
 6078            <body><a b={c()d}ˇ>
 6079                <script>
 6080                    var x = 1;<a b={c()d}ˇ
 6081                </script>
 6082            </body><a b={c()d}ˇ>
 6083        "#
 6084        .unindent(),
 6085    );
 6086    cx.update_editor(|editor, cx| {
 6087        editor.handle_input(">", cx);
 6088    });
 6089    cx.assert_editor_state(
 6090        &r#"
 6091            <body><a b={c()d}>ˇ
 6092                <script>
 6093                    var x = 1;<a b={c()d}>ˇ
 6094                </script>
 6095            </body><a b={c()d}>ˇ
 6096        "#
 6097        .unindent(),
 6098    );
 6099
 6100    // Reset
 6101    cx.set_state(
 6102        &r#"
 6103            <body>ˇ
 6104                <script>
 6105                    var x = 1;ˇ
 6106                </script>
 6107            </body>ˇ
 6108        "#
 6109        .unindent(),
 6110    );
 6111
 6112    cx.update_editor(|editor, cx| {
 6113        editor.handle_input("<", cx);
 6114    });
 6115    cx.assert_editor_state(
 6116        &r#"
 6117            <body><ˇ>
 6118                <script>
 6119                    var x = 1;<ˇ
 6120                </script>
 6121            </body><ˇ>
 6122        "#
 6123        .unindent(),
 6124    );
 6125
 6126    // When backspacing, the closing angle brackets are removed.
 6127    cx.update_editor(|editor, cx| {
 6128        editor.backspace(&Backspace, cx);
 6129    });
 6130    cx.assert_editor_state(
 6131        &r#"
 6132            <body>ˇ
 6133                <script>
 6134                    var x = 1;ˇ
 6135                </script>
 6136            </body>ˇ
 6137        "#
 6138        .unindent(),
 6139    );
 6140
 6141    // Block comments autoclose in JavaScript, but not HTML.
 6142    cx.update_editor(|editor, cx| {
 6143        editor.handle_input("/", cx);
 6144        editor.handle_input("*", cx);
 6145    });
 6146    cx.assert_editor_state(
 6147        &r#"
 6148            <body>/*ˇ
 6149                <script>
 6150                    var x = 1;/*ˇ */
 6151                </script>
 6152            </body>/*ˇ
 6153        "#
 6154        .unindent(),
 6155    );
 6156}
 6157
 6158#[gpui::test]
 6159async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6160    init_test(cx, |_| {});
 6161
 6162    let mut cx = EditorTestContext::new(cx).await;
 6163
 6164    let rust_language = Arc::new(
 6165        Language::new(
 6166            LanguageConfig {
 6167                name: "Rust".into(),
 6168                brackets: serde_json::from_value(json!([
 6169                    { "start": "{", "end": "}", "close": true, "newline": true },
 6170                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6171                ]))
 6172                .unwrap(),
 6173                autoclose_before: "})]>".into(),
 6174                ..Default::default()
 6175            },
 6176            Some(tree_sitter_rust::LANGUAGE.into()),
 6177        )
 6178        .with_override_query("(string_literal) @string")
 6179        .unwrap(),
 6180    );
 6181
 6182    cx.language_registry().add(rust_language.clone());
 6183    cx.update_buffer(|buffer, cx| {
 6184        buffer.set_language(Some(rust_language), cx);
 6185    });
 6186
 6187    cx.set_state(
 6188        &r#"
 6189            let x = ˇ
 6190        "#
 6191        .unindent(),
 6192    );
 6193
 6194    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6195    cx.update_editor(|editor, cx| {
 6196        editor.handle_input("\"", cx);
 6197    });
 6198    cx.assert_editor_state(
 6199        &r#"
 6200            let x = "ˇ"
 6201        "#
 6202        .unindent(),
 6203    );
 6204
 6205    // Inserting another quotation mark. The cursor moves across the existing
 6206    // automatically-inserted quotation mark.
 6207    cx.update_editor(|editor, cx| {
 6208        editor.handle_input("\"", cx);
 6209    });
 6210    cx.assert_editor_state(
 6211        &r#"
 6212            let x = ""ˇ
 6213        "#
 6214        .unindent(),
 6215    );
 6216
 6217    // Reset
 6218    cx.set_state(
 6219        &r#"
 6220            let x = ˇ
 6221        "#
 6222        .unindent(),
 6223    );
 6224
 6225    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6226    cx.update_editor(|editor, cx| {
 6227        editor.handle_input("\"", cx);
 6228        editor.handle_input(" ", cx);
 6229        editor.move_left(&Default::default(), cx);
 6230        editor.handle_input("\\", cx);
 6231        editor.handle_input("\"", cx);
 6232    });
 6233    cx.assert_editor_state(
 6234        &r#"
 6235            let x = "\"ˇ "
 6236        "#
 6237        .unindent(),
 6238    );
 6239
 6240    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6241    // mark. Nothing is inserted.
 6242    cx.update_editor(|editor, cx| {
 6243        editor.move_right(&Default::default(), cx);
 6244        editor.handle_input("\"", cx);
 6245    });
 6246    cx.assert_editor_state(
 6247        &r#"
 6248            let x = "\" "ˇ
 6249        "#
 6250        .unindent(),
 6251    );
 6252}
 6253
 6254#[gpui::test]
 6255async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6256    init_test(cx, |_| {});
 6257
 6258    let language = Arc::new(Language::new(
 6259        LanguageConfig {
 6260            brackets: BracketPairConfig {
 6261                pairs: vec![
 6262                    BracketPair {
 6263                        start: "{".to_string(),
 6264                        end: "}".to_string(),
 6265                        close: true,
 6266                        surround: true,
 6267                        newline: true,
 6268                    },
 6269                    BracketPair {
 6270                        start: "/* ".to_string(),
 6271                        end: "*/".to_string(),
 6272                        close: true,
 6273                        surround: true,
 6274                        ..Default::default()
 6275                    },
 6276                ],
 6277                ..Default::default()
 6278            },
 6279            ..Default::default()
 6280        },
 6281        Some(tree_sitter_rust::LANGUAGE.into()),
 6282    ));
 6283
 6284    let text = r#"
 6285        a
 6286        b
 6287        c
 6288    "#
 6289    .unindent();
 6290
 6291    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6293    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6294    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6295        .await;
 6296
 6297    view.update(cx, |view, cx| {
 6298        view.change_selections(None, cx, |s| {
 6299            s.select_display_ranges([
 6300                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6301                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6302                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6303            ])
 6304        });
 6305
 6306        view.handle_input("{", cx);
 6307        view.handle_input("{", cx);
 6308        view.handle_input("{", cx);
 6309        assert_eq!(
 6310            view.text(cx),
 6311            "
 6312                {{{a}}}
 6313                {{{b}}}
 6314                {{{c}}}
 6315            "
 6316            .unindent()
 6317        );
 6318        assert_eq!(
 6319            view.selections.display_ranges(cx),
 6320            [
 6321                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6322                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6323                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6324            ]
 6325        );
 6326
 6327        view.undo(&Undo, cx);
 6328        view.undo(&Undo, cx);
 6329        view.undo(&Undo, cx);
 6330        assert_eq!(
 6331            view.text(cx),
 6332            "
 6333                a
 6334                b
 6335                c
 6336            "
 6337            .unindent()
 6338        );
 6339        assert_eq!(
 6340            view.selections.display_ranges(cx),
 6341            [
 6342                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6343                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6345            ]
 6346        );
 6347
 6348        // Ensure inserting the first character of a multi-byte bracket pair
 6349        // doesn't surround the selections with the bracket.
 6350        view.handle_input("/", cx);
 6351        assert_eq!(
 6352            view.text(cx),
 6353            "
 6354                /
 6355                /
 6356                /
 6357            "
 6358            .unindent()
 6359        );
 6360        assert_eq!(
 6361            view.selections.display_ranges(cx),
 6362            [
 6363                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6364                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6365                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6366            ]
 6367        );
 6368
 6369        view.undo(&Undo, cx);
 6370        assert_eq!(
 6371            view.text(cx),
 6372            "
 6373                a
 6374                b
 6375                c
 6376            "
 6377            .unindent()
 6378        );
 6379        assert_eq!(
 6380            view.selections.display_ranges(cx),
 6381            [
 6382                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6383                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6384                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6385            ]
 6386        );
 6387
 6388        // Ensure inserting the last character of a multi-byte bracket pair
 6389        // doesn't surround the selections with the bracket.
 6390        view.handle_input("*", cx);
 6391        assert_eq!(
 6392            view.text(cx),
 6393            "
 6394                *
 6395                *
 6396                *
 6397            "
 6398            .unindent()
 6399        );
 6400        assert_eq!(
 6401            view.selections.display_ranges(cx),
 6402            [
 6403                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6404                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6405                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6406            ]
 6407        );
 6408    });
 6409}
 6410
 6411#[gpui::test]
 6412async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6413    init_test(cx, |_| {});
 6414
 6415    let language = Arc::new(Language::new(
 6416        LanguageConfig {
 6417            brackets: BracketPairConfig {
 6418                pairs: vec![BracketPair {
 6419                    start: "{".to_string(),
 6420                    end: "}".to_string(),
 6421                    close: true,
 6422                    surround: true,
 6423                    newline: true,
 6424                }],
 6425                ..Default::default()
 6426            },
 6427            autoclose_before: "}".to_string(),
 6428            ..Default::default()
 6429        },
 6430        Some(tree_sitter_rust::LANGUAGE.into()),
 6431    ));
 6432
 6433    let text = r#"
 6434        a
 6435        b
 6436        c
 6437    "#
 6438    .unindent();
 6439
 6440    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6441    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6442    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6443    editor
 6444        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6445        .await;
 6446
 6447    editor.update(cx, |editor, cx| {
 6448        editor.change_selections(None, cx, |s| {
 6449            s.select_ranges([
 6450                Point::new(0, 1)..Point::new(0, 1),
 6451                Point::new(1, 1)..Point::new(1, 1),
 6452                Point::new(2, 1)..Point::new(2, 1),
 6453            ])
 6454        });
 6455
 6456        editor.handle_input("{", cx);
 6457        editor.handle_input("{", cx);
 6458        editor.handle_input("_", cx);
 6459        assert_eq!(
 6460            editor.text(cx),
 6461            "
 6462                a{{_}}
 6463                b{{_}}
 6464                c{{_}}
 6465            "
 6466            .unindent()
 6467        );
 6468        assert_eq!(
 6469            editor.selections.ranges::<Point>(cx),
 6470            [
 6471                Point::new(0, 4)..Point::new(0, 4),
 6472                Point::new(1, 4)..Point::new(1, 4),
 6473                Point::new(2, 4)..Point::new(2, 4)
 6474            ]
 6475        );
 6476
 6477        editor.backspace(&Default::default(), cx);
 6478        editor.backspace(&Default::default(), cx);
 6479        assert_eq!(
 6480            editor.text(cx),
 6481            "
 6482                a{}
 6483                b{}
 6484                c{}
 6485            "
 6486            .unindent()
 6487        );
 6488        assert_eq!(
 6489            editor.selections.ranges::<Point>(cx),
 6490            [
 6491                Point::new(0, 2)..Point::new(0, 2),
 6492                Point::new(1, 2)..Point::new(1, 2),
 6493                Point::new(2, 2)..Point::new(2, 2)
 6494            ]
 6495        );
 6496
 6497        editor.delete_to_previous_word_start(&Default::default(), cx);
 6498        assert_eq!(
 6499            editor.text(cx),
 6500            "
 6501                a
 6502                b
 6503                c
 6504            "
 6505            .unindent()
 6506        );
 6507        assert_eq!(
 6508            editor.selections.ranges::<Point>(cx),
 6509            [
 6510                Point::new(0, 1)..Point::new(0, 1),
 6511                Point::new(1, 1)..Point::new(1, 1),
 6512                Point::new(2, 1)..Point::new(2, 1)
 6513            ]
 6514        );
 6515    });
 6516}
 6517
 6518#[gpui::test]
 6519async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6520    init_test(cx, |settings| {
 6521        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6522    });
 6523
 6524    let mut cx = EditorTestContext::new(cx).await;
 6525
 6526    let language = Arc::new(Language::new(
 6527        LanguageConfig {
 6528            brackets: BracketPairConfig {
 6529                pairs: vec![
 6530                    BracketPair {
 6531                        start: "{".to_string(),
 6532                        end: "}".to_string(),
 6533                        close: true,
 6534                        surround: true,
 6535                        newline: true,
 6536                    },
 6537                    BracketPair {
 6538                        start: "(".to_string(),
 6539                        end: ")".to_string(),
 6540                        close: true,
 6541                        surround: true,
 6542                        newline: true,
 6543                    },
 6544                    BracketPair {
 6545                        start: "[".to_string(),
 6546                        end: "]".to_string(),
 6547                        close: false,
 6548                        surround: true,
 6549                        newline: true,
 6550                    },
 6551                ],
 6552                ..Default::default()
 6553            },
 6554            autoclose_before: "})]".to_string(),
 6555            ..Default::default()
 6556        },
 6557        Some(tree_sitter_rust::LANGUAGE.into()),
 6558    ));
 6559
 6560    cx.language_registry().add(language.clone());
 6561    cx.update_buffer(|buffer, cx| {
 6562        buffer.set_language(Some(language), cx);
 6563    });
 6564
 6565    cx.set_state(
 6566        &"
 6567            {(ˇ)}
 6568            [[ˇ]]
 6569            {(ˇ)}
 6570        "
 6571        .unindent(),
 6572    );
 6573
 6574    cx.update_editor(|view, cx| {
 6575        view.backspace(&Default::default(), cx);
 6576        view.backspace(&Default::default(), cx);
 6577    });
 6578
 6579    cx.assert_editor_state(
 6580        &"
 6581            ˇ
 6582            ˇ]]
 6583            ˇ
 6584        "
 6585        .unindent(),
 6586    );
 6587
 6588    cx.update_editor(|view, cx| {
 6589        view.handle_input("{", cx);
 6590        view.handle_input("{", cx);
 6591        view.move_right(&MoveRight, cx);
 6592        view.move_right(&MoveRight, cx);
 6593        view.move_left(&MoveLeft, cx);
 6594        view.move_left(&MoveLeft, cx);
 6595        view.backspace(&Default::default(), cx);
 6596    });
 6597
 6598    cx.assert_editor_state(
 6599        &"
 6600            {ˇ}
 6601            {ˇ}]]
 6602            {ˇ}
 6603        "
 6604        .unindent(),
 6605    );
 6606
 6607    cx.update_editor(|view, cx| {
 6608        view.backspace(&Default::default(), cx);
 6609    });
 6610
 6611    cx.assert_editor_state(
 6612        &"
 6613            ˇ
 6614            ˇ]]
 6615            ˇ
 6616        "
 6617        .unindent(),
 6618    );
 6619}
 6620
 6621#[gpui::test]
 6622async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6623    init_test(cx, |_| {});
 6624
 6625    let language = Arc::new(Language::new(
 6626        LanguageConfig::default(),
 6627        Some(tree_sitter_rust::LANGUAGE.into()),
 6628    ));
 6629
 6630    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6631    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6632    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6633    editor
 6634        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6635        .await;
 6636
 6637    editor.update(cx, |editor, cx| {
 6638        editor.set_auto_replace_emoji_shortcode(true);
 6639
 6640        editor.handle_input("Hello ", cx);
 6641        editor.handle_input(":wave", cx);
 6642        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6643
 6644        editor.handle_input(":", cx);
 6645        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6646
 6647        editor.handle_input(" :smile", cx);
 6648        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6649
 6650        editor.handle_input(":", cx);
 6651        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6652
 6653        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6654        editor.handle_input(":wave", cx);
 6655        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6656
 6657        editor.handle_input(":", cx);
 6658        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6659
 6660        editor.handle_input(":1", cx);
 6661        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6662
 6663        editor.handle_input(":", cx);
 6664        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6665
 6666        // Ensure shortcode does not get replaced when it is part of a word
 6667        editor.handle_input(" Test:wave", cx);
 6668        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6669
 6670        editor.handle_input(":", cx);
 6671        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6672
 6673        editor.set_auto_replace_emoji_shortcode(false);
 6674
 6675        // Ensure shortcode does not get replaced when auto replace is off
 6676        editor.handle_input(" :wave", cx);
 6677        assert_eq!(
 6678            editor.text(cx),
 6679            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6680        );
 6681
 6682        editor.handle_input(":", cx);
 6683        assert_eq!(
 6684            editor.text(cx),
 6685            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6686        );
 6687    });
 6688}
 6689
 6690#[gpui::test]
 6691async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6692    init_test(cx, |_| {});
 6693
 6694    let (text, insertion_ranges) = marked_text_ranges(
 6695        indoc! {"
 6696            ˇ
 6697        "},
 6698        false,
 6699    );
 6700
 6701    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6702    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6703
 6704    _ = editor.update(cx, |editor, cx| {
 6705        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6706
 6707        editor
 6708            .insert_snippet(&insertion_ranges, snippet, cx)
 6709            .unwrap();
 6710
 6711        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6712            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6713            assert_eq!(editor.text(cx), expected_text);
 6714            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6715        }
 6716
 6717        assert(
 6718            editor,
 6719            cx,
 6720            indoc! {"
 6721            type «» =•
 6722            "},
 6723        );
 6724
 6725        assert!(editor.context_menu_visible(), "There should be a matches");
 6726    });
 6727}
 6728
 6729#[gpui::test]
 6730async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6731    init_test(cx, |_| {});
 6732
 6733    let (text, insertion_ranges) = marked_text_ranges(
 6734        indoc! {"
 6735            a.ˇ b
 6736            a.ˇ b
 6737            a.ˇ b
 6738        "},
 6739        false,
 6740    );
 6741
 6742    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6743    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6744
 6745    editor.update(cx, |editor, cx| {
 6746        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6747
 6748        editor
 6749            .insert_snippet(&insertion_ranges, snippet, cx)
 6750            .unwrap();
 6751
 6752        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6753            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6754            assert_eq!(editor.text(cx), expected_text);
 6755            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6756        }
 6757
 6758        assert(
 6759            editor,
 6760            cx,
 6761            indoc! {"
 6762                a.f(«one», two, «three») b
 6763                a.f(«one», two, «three») b
 6764                a.f(«one», two, «three») b
 6765            "},
 6766        );
 6767
 6768        // Can't move earlier than the first tab stop
 6769        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6770        assert(
 6771            editor,
 6772            cx,
 6773            indoc! {"
 6774                a.f(«one», two, «three») b
 6775                a.f(«one», two, «three») b
 6776                a.f(«one», two, «three») b
 6777            "},
 6778        );
 6779
 6780        assert!(editor.move_to_next_snippet_tabstop(cx));
 6781        assert(
 6782            editor,
 6783            cx,
 6784            indoc! {"
 6785                a.f(one, «two», three) b
 6786                a.f(one, «two», three) b
 6787                a.f(one, «two», three) b
 6788            "},
 6789        );
 6790
 6791        editor.move_to_prev_snippet_tabstop(cx);
 6792        assert(
 6793            editor,
 6794            cx,
 6795            indoc! {"
 6796                a.f(«one», two, «three») b
 6797                a.f(«one», two, «three») b
 6798                a.f(«one», two, «three») b
 6799            "},
 6800        );
 6801
 6802        assert!(editor.move_to_next_snippet_tabstop(cx));
 6803        assert(
 6804            editor,
 6805            cx,
 6806            indoc! {"
 6807                a.f(one, «two», three) b
 6808                a.f(one, «two», three) b
 6809                a.f(one, «two», three) b
 6810            "},
 6811        );
 6812        assert!(editor.move_to_next_snippet_tabstop(cx));
 6813        assert(
 6814            editor,
 6815            cx,
 6816            indoc! {"
 6817                a.f(one, two, three)ˇ b
 6818                a.f(one, two, three)ˇ b
 6819                a.f(one, two, three)ˇ b
 6820            "},
 6821        );
 6822
 6823        // As soon as the last tab stop is reached, snippet state is gone
 6824        editor.move_to_prev_snippet_tabstop(cx);
 6825        assert(
 6826            editor,
 6827            cx,
 6828            indoc! {"
 6829                a.f(one, two, three)ˇ b
 6830                a.f(one, two, three)ˇ b
 6831                a.f(one, two, three)ˇ b
 6832            "},
 6833        );
 6834    });
 6835}
 6836
 6837#[gpui::test]
 6838async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6839    init_test(cx, |_| {});
 6840
 6841    let fs = FakeFs::new(cx.executor());
 6842    fs.insert_file("/file.rs", Default::default()).await;
 6843
 6844    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6845
 6846    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6847    language_registry.add(rust_lang());
 6848    let mut fake_servers = language_registry.register_fake_lsp(
 6849        "Rust",
 6850        FakeLspAdapter {
 6851            capabilities: lsp::ServerCapabilities {
 6852                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6853                ..Default::default()
 6854            },
 6855            ..Default::default()
 6856        },
 6857    );
 6858
 6859    let buffer = project
 6860        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6861        .await
 6862        .unwrap();
 6863
 6864    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6865    let (editor, cx) =
 6866        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 6867    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6868    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6869
 6870    cx.executor().start_waiting();
 6871    let fake_server = fake_servers.next().await.unwrap();
 6872
 6873    let save = editor
 6874        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6875        .unwrap();
 6876    fake_server
 6877        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6878            assert_eq!(
 6879                params.text_document.uri,
 6880                lsp::Url::from_file_path("/file.rs").unwrap()
 6881            );
 6882            assert_eq!(params.options.tab_size, 4);
 6883            Ok(Some(vec![lsp::TextEdit::new(
 6884                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6885                ", ".to_string(),
 6886            )]))
 6887        })
 6888        .next()
 6889        .await;
 6890    cx.executor().start_waiting();
 6891    save.await;
 6892
 6893    assert_eq!(
 6894        editor.update(cx, |editor, cx| editor.text(cx)),
 6895        "one, two\nthree\n"
 6896    );
 6897    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6898
 6899    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6900    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6901
 6902    // Ensure we can still save even if formatting hangs.
 6903    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6904        assert_eq!(
 6905            params.text_document.uri,
 6906            lsp::Url::from_file_path("/file.rs").unwrap()
 6907        );
 6908        futures::future::pending::<()>().await;
 6909        unreachable!()
 6910    });
 6911    let save = editor
 6912        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6913        .unwrap();
 6914    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6915    cx.executor().start_waiting();
 6916    save.await;
 6917    assert_eq!(
 6918        editor.update(cx, |editor, cx| editor.text(cx)),
 6919        "one\ntwo\nthree\n"
 6920    );
 6921    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6922
 6923    // For non-dirty buffer, no formatting request should be sent
 6924    let save = editor
 6925        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6926        .unwrap();
 6927    let _pending_format_request = fake_server
 6928        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6929            panic!("Should not be invoked on non-dirty buffer");
 6930        })
 6931        .next();
 6932    cx.executor().start_waiting();
 6933    save.await;
 6934
 6935    // Set rust language override and assert overridden tabsize is sent to language server
 6936    update_test_language_settings(cx, |settings| {
 6937        settings.languages.insert(
 6938            "Rust".into(),
 6939            LanguageSettingsContent {
 6940                tab_size: NonZeroU32::new(8),
 6941                ..Default::default()
 6942            },
 6943        );
 6944    });
 6945
 6946    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6947    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6948    let save = editor
 6949        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6950        .unwrap();
 6951    fake_server
 6952        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6953            assert_eq!(
 6954                params.text_document.uri,
 6955                lsp::Url::from_file_path("/file.rs").unwrap()
 6956            );
 6957            assert_eq!(params.options.tab_size, 8);
 6958            Ok(Some(vec![]))
 6959        })
 6960        .next()
 6961        .await;
 6962    cx.executor().start_waiting();
 6963    save.await;
 6964}
 6965
 6966#[gpui::test]
 6967async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6968    init_test(cx, |_| {});
 6969
 6970    let cols = 4;
 6971    let rows = 10;
 6972    let sample_text_1 = sample_text(rows, cols, 'a');
 6973    assert_eq!(
 6974        sample_text_1,
 6975        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6976    );
 6977    let sample_text_2 = sample_text(rows, cols, 'l');
 6978    assert_eq!(
 6979        sample_text_2,
 6980        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6981    );
 6982    let sample_text_3 = sample_text(rows, cols, 'v');
 6983    assert_eq!(
 6984        sample_text_3,
 6985        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6986    );
 6987
 6988    let fs = FakeFs::new(cx.executor());
 6989    fs.insert_tree(
 6990        "/a",
 6991        json!({
 6992            "main.rs": sample_text_1,
 6993            "other.rs": sample_text_2,
 6994            "lib.rs": sample_text_3,
 6995        }),
 6996    )
 6997    .await;
 6998
 6999    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 7000    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7001    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7002
 7003    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7004    language_registry.add(rust_lang());
 7005    let mut fake_servers = language_registry.register_fake_lsp(
 7006        "Rust",
 7007        FakeLspAdapter {
 7008            capabilities: lsp::ServerCapabilities {
 7009                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7010                ..Default::default()
 7011            },
 7012            ..Default::default()
 7013        },
 7014    );
 7015
 7016    let worktree = project.update(cx, |project, cx| {
 7017        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7018        assert_eq!(worktrees.len(), 1);
 7019        worktrees.pop().unwrap()
 7020    });
 7021    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7022
 7023    let buffer_1 = project
 7024        .update(cx, |project, cx| {
 7025            project.open_buffer((worktree_id, "main.rs"), cx)
 7026        })
 7027        .await
 7028        .unwrap();
 7029    let buffer_2 = project
 7030        .update(cx, |project, cx| {
 7031            project.open_buffer((worktree_id, "other.rs"), cx)
 7032        })
 7033        .await
 7034        .unwrap();
 7035    let buffer_3 = project
 7036        .update(cx, |project, cx| {
 7037            project.open_buffer((worktree_id, "lib.rs"), cx)
 7038        })
 7039        .await
 7040        .unwrap();
 7041
 7042    let multi_buffer = cx.new_model(|cx| {
 7043        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7044        multi_buffer.push_excerpts(
 7045            buffer_1.clone(),
 7046            [
 7047                ExcerptRange {
 7048                    context: Point::new(0, 0)..Point::new(3, 0),
 7049                    primary: None,
 7050                },
 7051                ExcerptRange {
 7052                    context: Point::new(5, 0)..Point::new(7, 0),
 7053                    primary: None,
 7054                },
 7055                ExcerptRange {
 7056                    context: Point::new(9, 0)..Point::new(10, 4),
 7057                    primary: None,
 7058                },
 7059            ],
 7060            cx,
 7061        );
 7062        multi_buffer.push_excerpts(
 7063            buffer_2.clone(),
 7064            [
 7065                ExcerptRange {
 7066                    context: Point::new(0, 0)..Point::new(3, 0),
 7067                    primary: None,
 7068                },
 7069                ExcerptRange {
 7070                    context: Point::new(5, 0)..Point::new(7, 0),
 7071                    primary: None,
 7072                },
 7073                ExcerptRange {
 7074                    context: Point::new(9, 0)..Point::new(10, 4),
 7075                    primary: None,
 7076                },
 7077            ],
 7078            cx,
 7079        );
 7080        multi_buffer.push_excerpts(
 7081            buffer_3.clone(),
 7082            [
 7083                ExcerptRange {
 7084                    context: Point::new(0, 0)..Point::new(3, 0),
 7085                    primary: None,
 7086                },
 7087                ExcerptRange {
 7088                    context: Point::new(5, 0)..Point::new(7, 0),
 7089                    primary: None,
 7090                },
 7091                ExcerptRange {
 7092                    context: Point::new(9, 0)..Point::new(10, 4),
 7093                    primary: None,
 7094                },
 7095            ],
 7096            cx,
 7097        );
 7098        multi_buffer
 7099    });
 7100    let multi_buffer_editor = cx.new_view(|cx| {
 7101        Editor::new(
 7102            EditorMode::Full,
 7103            multi_buffer,
 7104            Some(project.clone()),
 7105            true,
 7106            cx,
 7107        )
 7108    });
 7109
 7110    multi_buffer_editor.update(cx, |editor, cx| {
 7111        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7112        editor.insert("|one|two|three|", cx);
 7113    });
 7114    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7115    multi_buffer_editor.update(cx, |editor, cx| {
 7116        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7117            s.select_ranges(Some(60..70))
 7118        });
 7119        editor.insert("|four|five|six|", cx);
 7120    });
 7121    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7122
 7123    // First two buffers should be edited, but not the third one.
 7124    assert_eq!(
 7125        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7126        "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}",
 7127    );
 7128    buffer_1.update(cx, |buffer, _| {
 7129        assert!(buffer.is_dirty());
 7130        assert_eq!(
 7131            buffer.text(),
 7132            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7133        )
 7134    });
 7135    buffer_2.update(cx, |buffer, _| {
 7136        assert!(buffer.is_dirty());
 7137        assert_eq!(
 7138            buffer.text(),
 7139            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7140        )
 7141    });
 7142    buffer_3.update(cx, |buffer, _| {
 7143        assert!(!buffer.is_dirty());
 7144        assert_eq!(buffer.text(), sample_text_3,)
 7145    });
 7146    cx.executor().run_until_parked();
 7147
 7148    cx.executor().start_waiting();
 7149    let save = multi_buffer_editor
 7150        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7151        .unwrap();
 7152
 7153    let fake_server = fake_servers.next().await.unwrap();
 7154    fake_server
 7155        .server
 7156        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7157            Ok(Some(vec![lsp::TextEdit::new(
 7158                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7159                format!("[{} formatted]", params.text_document.uri),
 7160            )]))
 7161        })
 7162        .detach();
 7163    save.await;
 7164
 7165    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7166    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7167    assert_eq!(
 7168        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7169        "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}",
 7170    );
 7171    buffer_1.update(cx, |buffer, _| {
 7172        assert!(!buffer.is_dirty());
 7173        assert_eq!(
 7174            buffer.text(),
 7175            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7176        )
 7177    });
 7178    buffer_2.update(cx, |buffer, _| {
 7179        assert!(!buffer.is_dirty());
 7180        assert_eq!(
 7181            buffer.text(),
 7182            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7183        )
 7184    });
 7185    buffer_3.update(cx, |buffer, _| {
 7186        assert!(!buffer.is_dirty());
 7187        assert_eq!(buffer.text(), sample_text_3,)
 7188    });
 7189}
 7190
 7191#[gpui::test]
 7192async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7193    init_test(cx, |_| {});
 7194
 7195    let fs = FakeFs::new(cx.executor());
 7196    fs.insert_file("/file.rs", Default::default()).await;
 7197
 7198    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7199
 7200    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7201    language_registry.add(rust_lang());
 7202    let mut fake_servers = language_registry.register_fake_lsp(
 7203        "Rust",
 7204        FakeLspAdapter {
 7205            capabilities: lsp::ServerCapabilities {
 7206                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7207                ..Default::default()
 7208            },
 7209            ..Default::default()
 7210        },
 7211    );
 7212
 7213    let buffer = project
 7214        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7215        .await
 7216        .unwrap();
 7217
 7218    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7219    let (editor, cx) =
 7220        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7221    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7222    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7223
 7224    cx.executor().start_waiting();
 7225    let fake_server = fake_servers.next().await.unwrap();
 7226
 7227    let save = editor
 7228        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7229        .unwrap();
 7230    fake_server
 7231        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7232            assert_eq!(
 7233                params.text_document.uri,
 7234                lsp::Url::from_file_path("/file.rs").unwrap()
 7235            );
 7236            assert_eq!(params.options.tab_size, 4);
 7237            Ok(Some(vec![lsp::TextEdit::new(
 7238                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7239                ", ".to_string(),
 7240            )]))
 7241        })
 7242        .next()
 7243        .await;
 7244    cx.executor().start_waiting();
 7245    save.await;
 7246    assert_eq!(
 7247        editor.update(cx, |editor, cx| editor.text(cx)),
 7248        "one, two\nthree\n"
 7249    );
 7250    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7251
 7252    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7253    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7254
 7255    // Ensure we can still save even if formatting hangs.
 7256    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7257        move |params, _| async move {
 7258            assert_eq!(
 7259                params.text_document.uri,
 7260                lsp::Url::from_file_path("/file.rs").unwrap()
 7261            );
 7262            futures::future::pending::<()>().await;
 7263            unreachable!()
 7264        },
 7265    );
 7266    let save = editor
 7267        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7268        .unwrap();
 7269    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7270    cx.executor().start_waiting();
 7271    save.await;
 7272    assert_eq!(
 7273        editor.update(cx, |editor, cx| editor.text(cx)),
 7274        "one\ntwo\nthree\n"
 7275    );
 7276    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7277
 7278    // For non-dirty buffer, no formatting request should be sent
 7279    let save = editor
 7280        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7281        .unwrap();
 7282    let _pending_format_request = fake_server
 7283        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7284            panic!("Should not be invoked on non-dirty buffer");
 7285        })
 7286        .next();
 7287    cx.executor().start_waiting();
 7288    save.await;
 7289
 7290    // Set Rust language override and assert overridden tabsize is sent to language server
 7291    update_test_language_settings(cx, |settings| {
 7292        settings.languages.insert(
 7293            "Rust".into(),
 7294            LanguageSettingsContent {
 7295                tab_size: NonZeroU32::new(8),
 7296                ..Default::default()
 7297            },
 7298        );
 7299    });
 7300
 7301    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7302    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7303    let save = editor
 7304        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7305        .unwrap();
 7306    fake_server
 7307        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7308            assert_eq!(
 7309                params.text_document.uri,
 7310                lsp::Url::from_file_path("/file.rs").unwrap()
 7311            );
 7312            assert_eq!(params.options.tab_size, 8);
 7313            Ok(Some(vec![]))
 7314        })
 7315        .next()
 7316        .await;
 7317    cx.executor().start_waiting();
 7318    save.await;
 7319}
 7320
 7321#[gpui::test]
 7322async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7323    init_test(cx, |settings| {
 7324        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7325            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7326        ))
 7327    });
 7328
 7329    let fs = FakeFs::new(cx.executor());
 7330    fs.insert_file("/file.rs", Default::default()).await;
 7331
 7332    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7333
 7334    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7335    language_registry.add(Arc::new(Language::new(
 7336        LanguageConfig {
 7337            name: "Rust".into(),
 7338            matcher: LanguageMatcher {
 7339                path_suffixes: vec!["rs".to_string()],
 7340                ..Default::default()
 7341            },
 7342            ..LanguageConfig::default()
 7343        },
 7344        Some(tree_sitter_rust::LANGUAGE.into()),
 7345    )));
 7346    update_test_language_settings(cx, |settings| {
 7347        // Enable Prettier formatting for the same buffer, and ensure
 7348        // LSP is called instead of Prettier.
 7349        settings.defaults.prettier = Some(PrettierSettings {
 7350            allowed: true,
 7351            ..PrettierSettings::default()
 7352        });
 7353    });
 7354    let mut fake_servers = language_registry.register_fake_lsp(
 7355        "Rust",
 7356        FakeLspAdapter {
 7357            capabilities: lsp::ServerCapabilities {
 7358                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7359                ..Default::default()
 7360            },
 7361            ..Default::default()
 7362        },
 7363    );
 7364
 7365    let buffer = project
 7366        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7367        .await
 7368        .unwrap();
 7369
 7370    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7371    let (editor, cx) =
 7372        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7373    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7374
 7375    cx.executor().start_waiting();
 7376    let fake_server = fake_servers.next().await.unwrap();
 7377
 7378    let format = editor
 7379        .update(cx, |editor, cx| {
 7380            editor.perform_format(
 7381                project.clone(),
 7382                FormatTrigger::Manual,
 7383                FormatTarget::Buffer,
 7384                cx,
 7385            )
 7386        })
 7387        .unwrap();
 7388    fake_server
 7389        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7390            assert_eq!(
 7391                params.text_document.uri,
 7392                lsp::Url::from_file_path("/file.rs").unwrap()
 7393            );
 7394            assert_eq!(params.options.tab_size, 4);
 7395            Ok(Some(vec![lsp::TextEdit::new(
 7396                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7397                ", ".to_string(),
 7398            )]))
 7399        })
 7400        .next()
 7401        .await;
 7402    cx.executor().start_waiting();
 7403    format.await;
 7404    assert_eq!(
 7405        editor.update(cx, |editor, cx| editor.text(cx)),
 7406        "one, two\nthree\n"
 7407    );
 7408
 7409    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7410    // Ensure we don't lock if formatting hangs.
 7411    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7412        assert_eq!(
 7413            params.text_document.uri,
 7414            lsp::Url::from_file_path("/file.rs").unwrap()
 7415        );
 7416        futures::future::pending::<()>().await;
 7417        unreachable!()
 7418    });
 7419    let format = editor
 7420        .update(cx, |editor, cx| {
 7421            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7422        })
 7423        .unwrap();
 7424    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7425    cx.executor().start_waiting();
 7426    format.await;
 7427    assert_eq!(
 7428        editor.update(cx, |editor, cx| editor.text(cx)),
 7429        "one\ntwo\nthree\n"
 7430    );
 7431}
 7432
 7433#[gpui::test]
 7434async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7435    init_test(cx, |_| {});
 7436
 7437    let mut cx = EditorLspTestContext::new_rust(
 7438        lsp::ServerCapabilities {
 7439            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7440            ..Default::default()
 7441        },
 7442        cx,
 7443    )
 7444    .await;
 7445
 7446    cx.set_state(indoc! {"
 7447        one.twoˇ
 7448    "});
 7449
 7450    // The format request takes a long time. When it completes, it inserts
 7451    // a newline and an indent before the `.`
 7452    cx.lsp
 7453        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7454            let executor = cx.background_executor().clone();
 7455            async move {
 7456                executor.timer(Duration::from_millis(100)).await;
 7457                Ok(Some(vec![lsp::TextEdit {
 7458                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7459                    new_text: "\n    ".into(),
 7460                }]))
 7461            }
 7462        });
 7463
 7464    // Submit a format request.
 7465    let format_1 = cx
 7466        .update_editor(|editor, cx| editor.format(&Format, cx))
 7467        .unwrap();
 7468    cx.executor().run_until_parked();
 7469
 7470    // Submit a second format request.
 7471    let format_2 = cx
 7472        .update_editor(|editor, cx| editor.format(&Format, cx))
 7473        .unwrap();
 7474    cx.executor().run_until_parked();
 7475
 7476    // Wait for both format requests to complete
 7477    cx.executor().advance_clock(Duration::from_millis(200));
 7478    cx.executor().start_waiting();
 7479    format_1.await.unwrap();
 7480    cx.executor().start_waiting();
 7481    format_2.await.unwrap();
 7482
 7483    // The formatting edits only happens once.
 7484    cx.assert_editor_state(indoc! {"
 7485        one
 7486            .twoˇ
 7487    "});
 7488}
 7489
 7490#[gpui::test]
 7491async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7492    init_test(cx, |settings| {
 7493        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7494    });
 7495
 7496    let mut cx = EditorLspTestContext::new_rust(
 7497        lsp::ServerCapabilities {
 7498            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7499            ..Default::default()
 7500        },
 7501        cx,
 7502    )
 7503    .await;
 7504
 7505    // Set up a buffer white some trailing whitespace and no trailing newline.
 7506    cx.set_state(
 7507        &[
 7508            "one ",   //
 7509            "twoˇ",   //
 7510            "three ", //
 7511            "four",   //
 7512        ]
 7513        .join("\n"),
 7514    );
 7515
 7516    // Submit a format request.
 7517    let format = cx
 7518        .update_editor(|editor, cx| editor.format(&Format, cx))
 7519        .unwrap();
 7520
 7521    // Record which buffer changes have been sent to the language server
 7522    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7523    cx.lsp
 7524        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7525            let buffer_changes = buffer_changes.clone();
 7526            move |params, _| {
 7527                buffer_changes.lock().extend(
 7528                    params
 7529                        .content_changes
 7530                        .into_iter()
 7531                        .map(|e| (e.range.unwrap(), e.text)),
 7532                );
 7533            }
 7534        });
 7535
 7536    // Handle formatting requests to the language server.
 7537    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7538        let buffer_changes = buffer_changes.clone();
 7539        move |_, _| {
 7540            // When formatting is requested, trailing whitespace has already been stripped,
 7541            // and the trailing newline has already been added.
 7542            assert_eq!(
 7543                &buffer_changes.lock()[1..],
 7544                &[
 7545                    (
 7546                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7547                        "".into()
 7548                    ),
 7549                    (
 7550                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7551                        "".into()
 7552                    ),
 7553                    (
 7554                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7555                        "\n".into()
 7556                    ),
 7557                ]
 7558            );
 7559
 7560            // Insert blank lines between each line of the buffer.
 7561            async move {
 7562                Ok(Some(vec![
 7563                    lsp::TextEdit {
 7564                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7565                        new_text: "\n".into(),
 7566                    },
 7567                    lsp::TextEdit {
 7568                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7569                        new_text: "\n".into(),
 7570                    },
 7571                ]))
 7572            }
 7573        }
 7574    });
 7575
 7576    // After formatting the buffer, the trailing whitespace is stripped,
 7577    // a newline is appended, and the edits provided by the language server
 7578    // have been applied.
 7579    format.await.unwrap();
 7580    cx.assert_editor_state(
 7581        &[
 7582            "one",   //
 7583            "",      //
 7584            "twoˇ",  //
 7585            "",      //
 7586            "three", //
 7587            "four",  //
 7588            "",      //
 7589        ]
 7590        .join("\n"),
 7591    );
 7592
 7593    // Undoing the formatting undoes the trailing whitespace removal, the
 7594    // trailing newline, and the LSP edits.
 7595    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7596    cx.assert_editor_state(
 7597        &[
 7598            "one ",   //
 7599            "twoˇ",   //
 7600            "three ", //
 7601            "four",   //
 7602        ]
 7603        .join("\n"),
 7604    );
 7605}
 7606
 7607#[gpui::test]
 7608async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7609    cx: &mut gpui::TestAppContext,
 7610) {
 7611    init_test(cx, |_| {});
 7612
 7613    cx.update(|cx| {
 7614        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7615            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7616                settings.auto_signature_help = Some(true);
 7617            });
 7618        });
 7619    });
 7620
 7621    let mut cx = EditorLspTestContext::new_rust(
 7622        lsp::ServerCapabilities {
 7623            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7624                ..Default::default()
 7625            }),
 7626            ..Default::default()
 7627        },
 7628        cx,
 7629    )
 7630    .await;
 7631
 7632    let language = Language::new(
 7633        LanguageConfig {
 7634            name: "Rust".into(),
 7635            brackets: BracketPairConfig {
 7636                pairs: vec![
 7637                    BracketPair {
 7638                        start: "{".to_string(),
 7639                        end: "}".to_string(),
 7640                        close: true,
 7641                        surround: true,
 7642                        newline: true,
 7643                    },
 7644                    BracketPair {
 7645                        start: "(".to_string(),
 7646                        end: ")".to_string(),
 7647                        close: true,
 7648                        surround: true,
 7649                        newline: true,
 7650                    },
 7651                    BracketPair {
 7652                        start: "/*".to_string(),
 7653                        end: " */".to_string(),
 7654                        close: true,
 7655                        surround: true,
 7656                        newline: true,
 7657                    },
 7658                    BracketPair {
 7659                        start: "[".to_string(),
 7660                        end: "]".to_string(),
 7661                        close: false,
 7662                        surround: false,
 7663                        newline: true,
 7664                    },
 7665                    BracketPair {
 7666                        start: "\"".to_string(),
 7667                        end: "\"".to_string(),
 7668                        close: true,
 7669                        surround: true,
 7670                        newline: false,
 7671                    },
 7672                    BracketPair {
 7673                        start: "<".to_string(),
 7674                        end: ">".to_string(),
 7675                        close: false,
 7676                        surround: true,
 7677                        newline: true,
 7678                    },
 7679                ],
 7680                ..Default::default()
 7681            },
 7682            autoclose_before: "})]".to_string(),
 7683            ..Default::default()
 7684        },
 7685        Some(tree_sitter_rust::LANGUAGE.into()),
 7686    );
 7687    let language = Arc::new(language);
 7688
 7689    cx.language_registry().add(language.clone());
 7690    cx.update_buffer(|buffer, cx| {
 7691        buffer.set_language(Some(language), cx);
 7692    });
 7693
 7694    cx.set_state(
 7695        &r#"
 7696            fn main() {
 7697                sampleˇ
 7698            }
 7699        "#
 7700        .unindent(),
 7701    );
 7702
 7703    cx.update_editor(|view, cx| {
 7704        view.handle_input("(", cx);
 7705    });
 7706    cx.assert_editor_state(
 7707        &"
 7708            fn main() {
 7709                sample(ˇ)
 7710            }
 7711        "
 7712        .unindent(),
 7713    );
 7714
 7715    let mocked_response = lsp::SignatureHelp {
 7716        signatures: vec![lsp::SignatureInformation {
 7717            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7718            documentation: None,
 7719            parameters: Some(vec![
 7720                lsp::ParameterInformation {
 7721                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7722                    documentation: None,
 7723                },
 7724                lsp::ParameterInformation {
 7725                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7726                    documentation: None,
 7727                },
 7728            ]),
 7729            active_parameter: None,
 7730        }],
 7731        active_signature: Some(0),
 7732        active_parameter: Some(0),
 7733    };
 7734    handle_signature_help_request(&mut cx, mocked_response).await;
 7735
 7736    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7737        .await;
 7738
 7739    cx.editor(|editor, _| {
 7740        let signature_help_state = editor.signature_help_state.popover().cloned();
 7741        assert!(signature_help_state.is_some());
 7742        let ParsedMarkdown {
 7743            text, highlights, ..
 7744        } = signature_help_state.unwrap().parsed_content;
 7745        assert_eq!(text, "param1: u8, param2: u8");
 7746        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7747    });
 7748}
 7749
 7750#[gpui::test]
 7751async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7752    init_test(cx, |_| {});
 7753
 7754    cx.update(|cx| {
 7755        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7756            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7757                settings.auto_signature_help = Some(false);
 7758                settings.show_signature_help_after_edits = Some(false);
 7759            });
 7760        });
 7761    });
 7762
 7763    let mut cx = EditorLspTestContext::new_rust(
 7764        lsp::ServerCapabilities {
 7765            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7766                ..Default::default()
 7767            }),
 7768            ..Default::default()
 7769        },
 7770        cx,
 7771    )
 7772    .await;
 7773
 7774    let language = Language::new(
 7775        LanguageConfig {
 7776            name: "Rust".into(),
 7777            brackets: BracketPairConfig {
 7778                pairs: vec![
 7779                    BracketPair {
 7780                        start: "{".to_string(),
 7781                        end: "}".to_string(),
 7782                        close: true,
 7783                        surround: true,
 7784                        newline: true,
 7785                    },
 7786                    BracketPair {
 7787                        start: "(".to_string(),
 7788                        end: ")".to_string(),
 7789                        close: true,
 7790                        surround: true,
 7791                        newline: true,
 7792                    },
 7793                    BracketPair {
 7794                        start: "/*".to_string(),
 7795                        end: " */".to_string(),
 7796                        close: true,
 7797                        surround: true,
 7798                        newline: true,
 7799                    },
 7800                    BracketPair {
 7801                        start: "[".to_string(),
 7802                        end: "]".to_string(),
 7803                        close: false,
 7804                        surround: false,
 7805                        newline: true,
 7806                    },
 7807                    BracketPair {
 7808                        start: "\"".to_string(),
 7809                        end: "\"".to_string(),
 7810                        close: true,
 7811                        surround: true,
 7812                        newline: false,
 7813                    },
 7814                    BracketPair {
 7815                        start: "<".to_string(),
 7816                        end: ">".to_string(),
 7817                        close: false,
 7818                        surround: true,
 7819                        newline: true,
 7820                    },
 7821                ],
 7822                ..Default::default()
 7823            },
 7824            autoclose_before: "})]".to_string(),
 7825            ..Default::default()
 7826        },
 7827        Some(tree_sitter_rust::LANGUAGE.into()),
 7828    );
 7829    let language = Arc::new(language);
 7830
 7831    cx.language_registry().add(language.clone());
 7832    cx.update_buffer(|buffer, cx| {
 7833        buffer.set_language(Some(language), cx);
 7834    });
 7835
 7836    // Ensure that signature_help is not called when no signature help is enabled.
 7837    cx.set_state(
 7838        &r#"
 7839            fn main() {
 7840                sampleˇ
 7841            }
 7842        "#
 7843        .unindent(),
 7844    );
 7845    cx.update_editor(|view, cx| {
 7846        view.handle_input("(", cx);
 7847    });
 7848    cx.assert_editor_state(
 7849        &"
 7850            fn main() {
 7851                sample(ˇ)
 7852            }
 7853        "
 7854        .unindent(),
 7855    );
 7856    cx.editor(|editor, _| {
 7857        assert!(editor.signature_help_state.task().is_none());
 7858    });
 7859
 7860    let mocked_response = lsp::SignatureHelp {
 7861        signatures: vec![lsp::SignatureInformation {
 7862            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7863            documentation: None,
 7864            parameters: Some(vec![
 7865                lsp::ParameterInformation {
 7866                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7867                    documentation: None,
 7868                },
 7869                lsp::ParameterInformation {
 7870                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7871                    documentation: None,
 7872                },
 7873            ]),
 7874            active_parameter: None,
 7875        }],
 7876        active_signature: Some(0),
 7877        active_parameter: Some(0),
 7878    };
 7879
 7880    // Ensure that signature_help is called when enabled afte edits
 7881    cx.update(|cx| {
 7882        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7883            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7884                settings.auto_signature_help = Some(false);
 7885                settings.show_signature_help_after_edits = Some(true);
 7886            });
 7887        });
 7888    });
 7889    cx.set_state(
 7890        &r#"
 7891            fn main() {
 7892                sampleˇ
 7893            }
 7894        "#
 7895        .unindent(),
 7896    );
 7897    cx.update_editor(|view, cx| {
 7898        view.handle_input("(", cx);
 7899    });
 7900    cx.assert_editor_state(
 7901        &"
 7902            fn main() {
 7903                sample(ˇ)
 7904            }
 7905        "
 7906        .unindent(),
 7907    );
 7908    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7909    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7910        .await;
 7911    cx.update_editor(|editor, _| {
 7912        let signature_help_state = editor.signature_help_state.popover().cloned();
 7913        assert!(signature_help_state.is_some());
 7914        let ParsedMarkdown {
 7915            text, highlights, ..
 7916        } = signature_help_state.unwrap().parsed_content;
 7917        assert_eq!(text, "param1: u8, param2: u8");
 7918        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7919        editor.signature_help_state = SignatureHelpState::default();
 7920    });
 7921
 7922    // Ensure that signature_help is called when auto signature help override is enabled
 7923    cx.update(|cx| {
 7924        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7925            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7926                settings.auto_signature_help = Some(true);
 7927                settings.show_signature_help_after_edits = Some(false);
 7928            });
 7929        });
 7930    });
 7931    cx.set_state(
 7932        &r#"
 7933            fn main() {
 7934                sampleˇ
 7935            }
 7936        "#
 7937        .unindent(),
 7938    );
 7939    cx.update_editor(|view, cx| {
 7940        view.handle_input("(", cx);
 7941    });
 7942    cx.assert_editor_state(
 7943        &"
 7944            fn main() {
 7945                sample(ˇ)
 7946            }
 7947        "
 7948        .unindent(),
 7949    );
 7950    handle_signature_help_request(&mut cx, mocked_response).await;
 7951    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7952        .await;
 7953    cx.editor(|editor, _| {
 7954        let signature_help_state = editor.signature_help_state.popover().cloned();
 7955        assert!(signature_help_state.is_some());
 7956        let ParsedMarkdown {
 7957            text, highlights, ..
 7958        } = signature_help_state.unwrap().parsed_content;
 7959        assert_eq!(text, "param1: u8, param2: u8");
 7960        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7961    });
 7962}
 7963
 7964#[gpui::test]
 7965async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7966    init_test(cx, |_| {});
 7967    cx.update(|cx| {
 7968        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7969            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7970                settings.auto_signature_help = Some(true);
 7971            });
 7972        });
 7973    });
 7974
 7975    let mut cx = EditorLspTestContext::new_rust(
 7976        lsp::ServerCapabilities {
 7977            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7978                ..Default::default()
 7979            }),
 7980            ..Default::default()
 7981        },
 7982        cx,
 7983    )
 7984    .await;
 7985
 7986    // A test that directly calls `show_signature_help`
 7987    cx.update_editor(|editor, cx| {
 7988        editor.show_signature_help(&ShowSignatureHelp, cx);
 7989    });
 7990
 7991    let mocked_response = lsp::SignatureHelp {
 7992        signatures: vec![lsp::SignatureInformation {
 7993            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7994            documentation: None,
 7995            parameters: Some(vec![
 7996                lsp::ParameterInformation {
 7997                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7998                    documentation: None,
 7999                },
 8000                lsp::ParameterInformation {
 8001                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8002                    documentation: None,
 8003                },
 8004            ]),
 8005            active_parameter: None,
 8006        }],
 8007        active_signature: Some(0),
 8008        active_parameter: Some(0),
 8009    };
 8010    handle_signature_help_request(&mut cx, mocked_response).await;
 8011
 8012    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8013        .await;
 8014
 8015    cx.editor(|editor, _| {
 8016        let signature_help_state = editor.signature_help_state.popover().cloned();
 8017        assert!(signature_help_state.is_some());
 8018        let ParsedMarkdown {
 8019            text, highlights, ..
 8020        } = signature_help_state.unwrap().parsed_content;
 8021        assert_eq!(text, "param1: u8, param2: u8");
 8022        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8023    });
 8024
 8025    // When exiting outside from inside the brackets, `signature_help` is closed.
 8026    cx.set_state(indoc! {"
 8027        fn main() {
 8028            sample(ˇ);
 8029        }
 8030
 8031        fn sample(param1: u8, param2: u8) {}
 8032    "});
 8033
 8034    cx.update_editor(|editor, cx| {
 8035        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8036    });
 8037
 8038    let mocked_response = lsp::SignatureHelp {
 8039        signatures: Vec::new(),
 8040        active_signature: None,
 8041        active_parameter: None,
 8042    };
 8043    handle_signature_help_request(&mut cx, mocked_response).await;
 8044
 8045    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8046        .await;
 8047
 8048    cx.editor(|editor, _| {
 8049        assert!(!editor.signature_help_state.is_shown());
 8050    });
 8051
 8052    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8053    cx.set_state(indoc! {"
 8054        fn main() {
 8055            sample(ˇ);
 8056        }
 8057
 8058        fn sample(param1: u8, param2: u8) {}
 8059    "});
 8060
 8061    let mocked_response = lsp::SignatureHelp {
 8062        signatures: vec![lsp::SignatureInformation {
 8063            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8064            documentation: None,
 8065            parameters: Some(vec![
 8066                lsp::ParameterInformation {
 8067                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8068                    documentation: None,
 8069                },
 8070                lsp::ParameterInformation {
 8071                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8072                    documentation: None,
 8073                },
 8074            ]),
 8075            active_parameter: None,
 8076        }],
 8077        active_signature: Some(0),
 8078        active_parameter: Some(0),
 8079    };
 8080    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8081    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8082        .await;
 8083    cx.editor(|editor, _| {
 8084        assert!(editor.signature_help_state.is_shown());
 8085    });
 8086
 8087    // Restore the popover with more parameter input
 8088    cx.set_state(indoc! {"
 8089        fn main() {
 8090            sample(param1, param2ˇ);
 8091        }
 8092
 8093        fn sample(param1: u8, param2: u8) {}
 8094    "});
 8095
 8096    let mocked_response = lsp::SignatureHelp {
 8097        signatures: vec![lsp::SignatureInformation {
 8098            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8099            documentation: None,
 8100            parameters: Some(vec![
 8101                lsp::ParameterInformation {
 8102                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8103                    documentation: None,
 8104                },
 8105                lsp::ParameterInformation {
 8106                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8107                    documentation: None,
 8108                },
 8109            ]),
 8110            active_parameter: None,
 8111        }],
 8112        active_signature: Some(0),
 8113        active_parameter: Some(1),
 8114    };
 8115    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8116    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8117        .await;
 8118
 8119    // When selecting a range, the popover is gone.
 8120    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8121    cx.update_editor(|editor, cx| {
 8122        editor.change_selections(None, cx, |s| {
 8123            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8124        })
 8125    });
 8126    cx.assert_editor_state(indoc! {"
 8127        fn main() {
 8128            sample(param1, «ˇparam2»);
 8129        }
 8130
 8131        fn sample(param1: u8, param2: u8) {}
 8132    "});
 8133    cx.editor(|editor, _| {
 8134        assert!(!editor.signature_help_state.is_shown());
 8135    });
 8136
 8137    // When unselecting again, the popover is back if within the brackets.
 8138    cx.update_editor(|editor, cx| {
 8139        editor.change_selections(None, cx, |s| {
 8140            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8141        })
 8142    });
 8143    cx.assert_editor_state(indoc! {"
 8144        fn main() {
 8145            sample(param1, ˇparam2);
 8146        }
 8147
 8148        fn sample(param1: u8, param2: u8) {}
 8149    "});
 8150    handle_signature_help_request(&mut cx, mocked_response).await;
 8151    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8152        .await;
 8153    cx.editor(|editor, _| {
 8154        assert!(editor.signature_help_state.is_shown());
 8155    });
 8156
 8157    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8158    cx.update_editor(|editor, cx| {
 8159        editor.change_selections(None, cx, |s| {
 8160            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8161            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8162        })
 8163    });
 8164    cx.assert_editor_state(indoc! {"
 8165        fn main() {
 8166            sample(param1, ˇparam2);
 8167        }
 8168
 8169        fn sample(param1: u8, param2: u8) {}
 8170    "});
 8171
 8172    let mocked_response = lsp::SignatureHelp {
 8173        signatures: vec![lsp::SignatureInformation {
 8174            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8175            documentation: None,
 8176            parameters: Some(vec![
 8177                lsp::ParameterInformation {
 8178                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8179                    documentation: None,
 8180                },
 8181                lsp::ParameterInformation {
 8182                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8183                    documentation: None,
 8184                },
 8185            ]),
 8186            active_parameter: None,
 8187        }],
 8188        active_signature: Some(0),
 8189        active_parameter: Some(1),
 8190    };
 8191    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8192    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8193        .await;
 8194    cx.update_editor(|editor, cx| {
 8195        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8196    });
 8197    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8198        .await;
 8199    cx.update_editor(|editor, cx| {
 8200        editor.change_selections(None, cx, |s| {
 8201            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8202        })
 8203    });
 8204    cx.assert_editor_state(indoc! {"
 8205        fn main() {
 8206            sample(param1, «ˇparam2»);
 8207        }
 8208
 8209        fn sample(param1: u8, param2: u8) {}
 8210    "});
 8211    cx.update_editor(|editor, cx| {
 8212        editor.change_selections(None, cx, |s| {
 8213            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8214        })
 8215    });
 8216    cx.assert_editor_state(indoc! {"
 8217        fn main() {
 8218            sample(param1, ˇparam2);
 8219        }
 8220
 8221        fn sample(param1: u8, param2: u8) {}
 8222    "});
 8223    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8224        .await;
 8225}
 8226
 8227#[gpui::test]
 8228async fn test_completion(cx: &mut gpui::TestAppContext) {
 8229    init_test(cx, |_| {});
 8230
 8231    let mut cx = EditorLspTestContext::new_rust(
 8232        lsp::ServerCapabilities {
 8233            completion_provider: Some(lsp::CompletionOptions {
 8234                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8235                resolve_provider: Some(true),
 8236                ..Default::default()
 8237            }),
 8238            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8239            ..Default::default()
 8240        },
 8241        cx,
 8242    )
 8243    .await;
 8244    let counter = Arc::new(AtomicUsize::new(0));
 8245
 8246    cx.set_state(indoc! {"
 8247        oneˇ
 8248        two
 8249        three
 8250    "});
 8251    cx.simulate_keystroke(".");
 8252    handle_completion_request(
 8253        &mut cx,
 8254        indoc! {"
 8255            one.|<>
 8256            two
 8257            three
 8258        "},
 8259        vec!["first_completion", "second_completion"],
 8260        counter.clone(),
 8261    )
 8262    .await;
 8263    cx.condition(|editor, _| editor.context_menu_visible())
 8264        .await;
 8265    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8266
 8267    let _handler = handle_signature_help_request(
 8268        &mut cx,
 8269        lsp::SignatureHelp {
 8270            signatures: vec![lsp::SignatureInformation {
 8271                label: "test signature".to_string(),
 8272                documentation: None,
 8273                parameters: Some(vec![lsp::ParameterInformation {
 8274                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8275                    documentation: None,
 8276                }]),
 8277                active_parameter: None,
 8278            }],
 8279            active_signature: None,
 8280            active_parameter: None,
 8281        },
 8282    );
 8283    cx.update_editor(|editor, cx| {
 8284        assert!(
 8285            !editor.signature_help_state.is_shown(),
 8286            "No signature help was called for"
 8287        );
 8288        editor.show_signature_help(&ShowSignatureHelp, cx);
 8289    });
 8290    cx.run_until_parked();
 8291    cx.update_editor(|editor, _| {
 8292        assert!(
 8293            !editor.signature_help_state.is_shown(),
 8294            "No signature help should be shown when completions menu is open"
 8295        );
 8296    });
 8297
 8298    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8299        editor.context_menu_next(&Default::default(), cx);
 8300        editor
 8301            .confirm_completion(&ConfirmCompletion::default(), cx)
 8302            .unwrap()
 8303    });
 8304    cx.assert_editor_state(indoc! {"
 8305        one.second_completionˇ
 8306        two
 8307        three
 8308    "});
 8309
 8310    handle_resolve_completion_request(
 8311        &mut cx,
 8312        Some(vec![
 8313            (
 8314                //This overlaps with the primary completion edit which is
 8315                //misbehavior from the LSP spec, test that we filter it out
 8316                indoc! {"
 8317                    one.second_ˇcompletion
 8318                    two
 8319                    threeˇ
 8320                "},
 8321                "overlapping additional edit",
 8322            ),
 8323            (
 8324                indoc! {"
 8325                    one.second_completion
 8326                    two
 8327                    threeˇ
 8328                "},
 8329                "\nadditional edit",
 8330            ),
 8331        ]),
 8332    )
 8333    .await;
 8334    apply_additional_edits.await.unwrap();
 8335    cx.assert_editor_state(indoc! {"
 8336        one.second_completionˇ
 8337        two
 8338        three
 8339        additional edit
 8340    "});
 8341
 8342    cx.set_state(indoc! {"
 8343        one.second_completion
 8344        twoˇ
 8345        threeˇ
 8346        additional edit
 8347    "});
 8348    cx.simulate_keystroke(" ");
 8349    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8350    cx.simulate_keystroke("s");
 8351    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8352
 8353    cx.assert_editor_state(indoc! {"
 8354        one.second_completion
 8355        two sˇ
 8356        three sˇ
 8357        additional edit
 8358    "});
 8359    handle_completion_request(
 8360        &mut cx,
 8361        indoc! {"
 8362            one.second_completion
 8363            two s
 8364            three <s|>
 8365            additional edit
 8366        "},
 8367        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8368        counter.clone(),
 8369    )
 8370    .await;
 8371    cx.condition(|editor, _| editor.context_menu_visible())
 8372        .await;
 8373    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8374
 8375    cx.simulate_keystroke("i");
 8376
 8377    handle_completion_request(
 8378        &mut cx,
 8379        indoc! {"
 8380            one.second_completion
 8381            two si
 8382            three <si|>
 8383            additional edit
 8384        "},
 8385        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8386        counter.clone(),
 8387    )
 8388    .await;
 8389    cx.condition(|editor, _| editor.context_menu_visible())
 8390        .await;
 8391    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8392
 8393    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8394        editor
 8395            .confirm_completion(&ConfirmCompletion::default(), cx)
 8396            .unwrap()
 8397    });
 8398    cx.assert_editor_state(indoc! {"
 8399        one.second_completion
 8400        two sixth_completionˇ
 8401        three sixth_completionˇ
 8402        additional edit
 8403    "});
 8404
 8405    apply_additional_edits.await.unwrap();
 8406
 8407    update_test_language_settings(&mut cx, |settings| {
 8408        settings.defaults.show_completions_on_input = Some(false);
 8409    });
 8410    cx.set_state("editorˇ");
 8411    cx.simulate_keystroke(".");
 8412    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8413    cx.simulate_keystroke("c");
 8414    cx.simulate_keystroke("l");
 8415    cx.simulate_keystroke("o");
 8416    cx.assert_editor_state("editor.cloˇ");
 8417    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8418    cx.update_editor(|editor, cx| {
 8419        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8420    });
 8421    handle_completion_request(
 8422        &mut cx,
 8423        "editor.<clo|>",
 8424        vec!["close", "clobber"],
 8425        counter.clone(),
 8426    )
 8427    .await;
 8428    cx.condition(|editor, _| editor.context_menu_visible())
 8429        .await;
 8430    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8431
 8432    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8433        editor
 8434            .confirm_completion(&ConfirmCompletion::default(), cx)
 8435            .unwrap()
 8436    });
 8437    cx.assert_editor_state("editor.closeˇ");
 8438    handle_resolve_completion_request(&mut cx, None).await;
 8439    apply_additional_edits.await.unwrap();
 8440}
 8441
 8442#[gpui::test]
 8443async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8444    init_test(cx, |_| {});
 8445    let mut cx = EditorLspTestContext::new_rust(
 8446        lsp::ServerCapabilities {
 8447            completion_provider: Some(lsp::CompletionOptions {
 8448                trigger_characters: Some(vec![".".to_string()]),
 8449                ..Default::default()
 8450            }),
 8451            ..Default::default()
 8452        },
 8453        cx,
 8454    )
 8455    .await;
 8456    cx.lsp
 8457        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8458            Ok(Some(lsp::CompletionResponse::Array(vec![
 8459                lsp::CompletionItem {
 8460                    label: "first".into(),
 8461                    ..Default::default()
 8462                },
 8463                lsp::CompletionItem {
 8464                    label: "last".into(),
 8465                    ..Default::default()
 8466                },
 8467            ])))
 8468        });
 8469    cx.set_state("variableˇ");
 8470    cx.simulate_keystroke(".");
 8471    cx.executor().run_until_parked();
 8472
 8473    cx.update_editor(|editor, _| {
 8474        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8475        {
 8476            assert_eq!(completion_menu_entries(&menu.entries), &["first", "last"]);
 8477        } else {
 8478            panic!("expected completion menu to be open");
 8479        }
 8480    });
 8481
 8482    cx.update_editor(|editor, cx| {
 8483        editor.move_page_down(&MovePageDown::default(), cx);
 8484        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8485        {
 8486            assert!(
 8487                menu.selected_item == 1,
 8488                "expected PageDown to select the last item from the context menu"
 8489            );
 8490        } else {
 8491            panic!("expected completion menu to stay open after PageDown");
 8492        }
 8493    });
 8494
 8495    cx.update_editor(|editor, cx| {
 8496        editor.move_page_up(&MovePageUp::default(), cx);
 8497        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8498        {
 8499            assert!(
 8500                menu.selected_item == 0,
 8501                "expected PageUp to select the first item from the context menu"
 8502            );
 8503        } else {
 8504            panic!("expected completion menu to stay open after PageUp");
 8505        }
 8506    });
 8507}
 8508
 8509#[gpui::test]
 8510async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8511    init_test(cx, |_| {});
 8512    let mut cx = EditorLspTestContext::new_rust(
 8513        lsp::ServerCapabilities {
 8514            completion_provider: Some(lsp::CompletionOptions {
 8515                trigger_characters: Some(vec![".".to_string()]),
 8516                ..Default::default()
 8517            }),
 8518            ..Default::default()
 8519        },
 8520        cx,
 8521    )
 8522    .await;
 8523    cx.lsp
 8524        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8525            Ok(Some(lsp::CompletionResponse::Array(vec![
 8526                lsp::CompletionItem {
 8527                    label: "Range".into(),
 8528                    sort_text: Some("a".into()),
 8529                    ..Default::default()
 8530                },
 8531                lsp::CompletionItem {
 8532                    label: "r".into(),
 8533                    sort_text: Some("b".into()),
 8534                    ..Default::default()
 8535                },
 8536                lsp::CompletionItem {
 8537                    label: "ret".into(),
 8538                    sort_text: Some("c".into()),
 8539                    ..Default::default()
 8540                },
 8541                lsp::CompletionItem {
 8542                    label: "return".into(),
 8543                    sort_text: Some("d".into()),
 8544                    ..Default::default()
 8545                },
 8546                lsp::CompletionItem {
 8547                    label: "slice".into(),
 8548                    sort_text: Some("d".into()),
 8549                    ..Default::default()
 8550                },
 8551            ])))
 8552        });
 8553    cx.set_state("");
 8554    cx.executor().run_until_parked();
 8555    cx.update_editor(|editor, cx| {
 8556        editor.show_completions(
 8557            &ShowCompletions {
 8558                trigger: Some("r".into()),
 8559            },
 8560            cx,
 8561        );
 8562    });
 8563    cx.executor().run_until_parked();
 8564
 8565    cx.update_editor(|editor, _| {
 8566        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8567        {
 8568            assert_eq!(
 8569                completion_menu_entries(&menu.entries),
 8570                &["r", "ret", "Range", "return"]
 8571            );
 8572        } else {
 8573            panic!("expected completion menu to be open");
 8574        }
 8575    });
 8576}
 8577
 8578#[gpui::test]
 8579async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8580    init_test(cx, |_| {});
 8581
 8582    let mut cx = EditorLspTestContext::new_rust(
 8583        lsp::ServerCapabilities {
 8584            completion_provider: Some(lsp::CompletionOptions {
 8585                trigger_characters: Some(vec![".".to_string()]),
 8586                resolve_provider: Some(true),
 8587                ..Default::default()
 8588            }),
 8589            ..Default::default()
 8590        },
 8591        cx,
 8592    )
 8593    .await;
 8594
 8595    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8596    cx.simulate_keystroke(".");
 8597    let completion_item = lsp::CompletionItem {
 8598        label: "Some".into(),
 8599        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8600        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8601        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8602            kind: lsp::MarkupKind::Markdown,
 8603            value: "```rust\nSome(2)\n```".to_string(),
 8604        })),
 8605        deprecated: Some(false),
 8606        sort_text: Some("Some".to_string()),
 8607        filter_text: Some("Some".to_string()),
 8608        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8609        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8610            range: lsp::Range {
 8611                start: lsp::Position {
 8612                    line: 0,
 8613                    character: 22,
 8614                },
 8615                end: lsp::Position {
 8616                    line: 0,
 8617                    character: 22,
 8618                },
 8619            },
 8620            new_text: "Some(2)".to_string(),
 8621        })),
 8622        additional_text_edits: Some(vec![lsp::TextEdit {
 8623            range: lsp::Range {
 8624                start: lsp::Position {
 8625                    line: 0,
 8626                    character: 20,
 8627                },
 8628                end: lsp::Position {
 8629                    line: 0,
 8630                    character: 22,
 8631                },
 8632            },
 8633            new_text: "".to_string(),
 8634        }]),
 8635        ..Default::default()
 8636    };
 8637
 8638    let closure_completion_item = completion_item.clone();
 8639    let counter = Arc::new(AtomicUsize::new(0));
 8640    let counter_clone = counter.clone();
 8641    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8642        let task_completion_item = closure_completion_item.clone();
 8643        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8644        async move {
 8645            Ok(Some(lsp::CompletionResponse::Array(vec![
 8646                task_completion_item,
 8647            ])))
 8648        }
 8649    });
 8650
 8651    cx.condition(|editor, _| editor.context_menu_visible())
 8652        .await;
 8653    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8654    assert!(request.next().await.is_some());
 8655    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8656
 8657    cx.simulate_keystroke("S");
 8658    cx.simulate_keystroke("o");
 8659    cx.simulate_keystroke("m");
 8660    cx.condition(|editor, _| editor.context_menu_visible())
 8661        .await;
 8662    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8663    assert!(request.next().await.is_some());
 8664    assert!(request.next().await.is_some());
 8665    assert!(request.next().await.is_some());
 8666    request.close();
 8667    assert!(request.next().await.is_none());
 8668    assert_eq!(
 8669        counter.load(atomic::Ordering::Acquire),
 8670        4,
 8671        "With the completions menu open, only one LSP request should happen per input"
 8672    );
 8673}
 8674
 8675#[gpui::test]
 8676async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8677    init_test(cx, |_| {});
 8678    let mut cx = EditorTestContext::new(cx).await;
 8679    let language = Arc::new(Language::new(
 8680        LanguageConfig {
 8681            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8682            ..Default::default()
 8683        },
 8684        Some(tree_sitter_rust::LANGUAGE.into()),
 8685    ));
 8686    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8687
 8688    // If multiple selections intersect a line, the line is only toggled once.
 8689    cx.set_state(indoc! {"
 8690        fn a() {
 8691            «//b();
 8692            ˇ»// «c();
 8693            //ˇ»  d();
 8694        }
 8695    "});
 8696
 8697    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8698
 8699    cx.assert_editor_state(indoc! {"
 8700        fn a() {
 8701            «b();
 8702            c();
 8703            ˇ» d();
 8704        }
 8705    "});
 8706
 8707    // The comment prefix is inserted at the same column for every line in a
 8708    // selection.
 8709    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8710
 8711    cx.assert_editor_state(indoc! {"
 8712        fn a() {
 8713            // «b();
 8714            // c();
 8715            ˇ»//  d();
 8716        }
 8717    "});
 8718
 8719    // If a selection ends at the beginning of a line, that line is not toggled.
 8720    cx.set_selections_state(indoc! {"
 8721        fn a() {
 8722            // b();
 8723            «// c();
 8724        ˇ»    //  d();
 8725        }
 8726    "});
 8727
 8728    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8729
 8730    cx.assert_editor_state(indoc! {"
 8731        fn a() {
 8732            // b();
 8733            «c();
 8734        ˇ»    //  d();
 8735        }
 8736    "});
 8737
 8738    // If a selection span a single line and is empty, the line is toggled.
 8739    cx.set_state(indoc! {"
 8740        fn a() {
 8741            a();
 8742            b();
 8743        ˇ
 8744        }
 8745    "});
 8746
 8747    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8748
 8749    cx.assert_editor_state(indoc! {"
 8750        fn a() {
 8751            a();
 8752            b();
 8753        //•ˇ
 8754        }
 8755    "});
 8756
 8757    // If a selection span multiple lines, empty lines are not toggled.
 8758    cx.set_state(indoc! {"
 8759        fn a() {
 8760            «a();
 8761
 8762            c();ˇ»
 8763        }
 8764    "});
 8765
 8766    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8767
 8768    cx.assert_editor_state(indoc! {"
 8769        fn a() {
 8770            // «a();
 8771
 8772            // c();ˇ»
 8773        }
 8774    "});
 8775
 8776    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8777    cx.set_state(indoc! {"
 8778        fn a() {
 8779            «// a();
 8780            /// b();
 8781            //! c();ˇ»
 8782        }
 8783    "});
 8784
 8785    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8786
 8787    cx.assert_editor_state(indoc! {"
 8788        fn a() {
 8789            «a();
 8790            b();
 8791            c();ˇ»
 8792        }
 8793    "});
 8794}
 8795
 8796#[gpui::test]
 8797async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8798    init_test(cx, |_| {});
 8799    let mut cx = EditorTestContext::new(cx).await;
 8800    let language = Arc::new(Language::new(
 8801        LanguageConfig {
 8802            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8803            ..Default::default()
 8804        },
 8805        Some(tree_sitter_rust::LANGUAGE.into()),
 8806    ));
 8807    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8808
 8809    let toggle_comments = &ToggleComments {
 8810        advance_downwards: false,
 8811        ignore_indent: true,
 8812    };
 8813
 8814    // If multiple selections intersect a line, the line is only toggled once.
 8815    cx.set_state(indoc! {"
 8816        fn a() {
 8817        //    «b();
 8818        //    c();
 8819        //    ˇ» d();
 8820        }
 8821    "});
 8822
 8823    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8824
 8825    cx.assert_editor_state(indoc! {"
 8826        fn a() {
 8827            «b();
 8828            c();
 8829            ˇ» d();
 8830        }
 8831    "});
 8832
 8833    // The comment prefix is inserted at the beginning of each line
 8834    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8835
 8836    cx.assert_editor_state(indoc! {"
 8837        fn a() {
 8838        //    «b();
 8839        //    c();
 8840        //    ˇ» d();
 8841        }
 8842    "});
 8843
 8844    // If a selection ends at the beginning of a line, that line is not toggled.
 8845    cx.set_selections_state(indoc! {"
 8846        fn a() {
 8847        //    b();
 8848        //    «c();
 8849        ˇ»//     d();
 8850        }
 8851    "});
 8852
 8853    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8854
 8855    cx.assert_editor_state(indoc! {"
 8856        fn a() {
 8857        //    b();
 8858            «c();
 8859        ˇ»//     d();
 8860        }
 8861    "});
 8862
 8863    // If a selection span a single line and is empty, the line is toggled.
 8864    cx.set_state(indoc! {"
 8865        fn a() {
 8866            a();
 8867            b();
 8868        ˇ
 8869        }
 8870    "});
 8871
 8872    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8873
 8874    cx.assert_editor_state(indoc! {"
 8875        fn a() {
 8876            a();
 8877            b();
 8878        //ˇ
 8879        }
 8880    "});
 8881
 8882    // If a selection span multiple lines, empty lines are not toggled.
 8883    cx.set_state(indoc! {"
 8884        fn a() {
 8885            «a();
 8886
 8887            c();ˇ»
 8888        }
 8889    "});
 8890
 8891    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8892
 8893    cx.assert_editor_state(indoc! {"
 8894        fn a() {
 8895        //    «a();
 8896
 8897        //    c();ˇ»
 8898        }
 8899    "});
 8900
 8901    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8902    cx.set_state(indoc! {"
 8903        fn a() {
 8904        //    «a();
 8905        ///    b();
 8906        //!    c();ˇ»
 8907        }
 8908    "});
 8909
 8910    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8911
 8912    cx.assert_editor_state(indoc! {"
 8913        fn a() {
 8914            «a();
 8915            b();
 8916            c();ˇ»
 8917        }
 8918    "});
 8919}
 8920
 8921#[gpui::test]
 8922async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8923    init_test(cx, |_| {});
 8924
 8925    let language = Arc::new(Language::new(
 8926        LanguageConfig {
 8927            line_comments: vec!["// ".into()],
 8928            ..Default::default()
 8929        },
 8930        Some(tree_sitter_rust::LANGUAGE.into()),
 8931    ));
 8932
 8933    let mut cx = EditorTestContext::new(cx).await;
 8934
 8935    cx.language_registry().add(language.clone());
 8936    cx.update_buffer(|buffer, cx| {
 8937        buffer.set_language(Some(language), cx);
 8938    });
 8939
 8940    let toggle_comments = &ToggleComments {
 8941        advance_downwards: true,
 8942        ignore_indent: false,
 8943    };
 8944
 8945    // Single cursor on one line -> advance
 8946    // Cursor moves horizontally 3 characters as well on non-blank line
 8947    cx.set_state(indoc!(
 8948        "fn a() {
 8949             ˇdog();
 8950             cat();
 8951        }"
 8952    ));
 8953    cx.update_editor(|editor, cx| {
 8954        editor.toggle_comments(toggle_comments, cx);
 8955    });
 8956    cx.assert_editor_state(indoc!(
 8957        "fn a() {
 8958             // dog();
 8959             catˇ();
 8960        }"
 8961    ));
 8962
 8963    // Single selection on one line -> don't advance
 8964    cx.set_state(indoc!(
 8965        "fn a() {
 8966             «dog()ˇ»;
 8967             cat();
 8968        }"
 8969    ));
 8970    cx.update_editor(|editor, cx| {
 8971        editor.toggle_comments(toggle_comments, cx);
 8972    });
 8973    cx.assert_editor_state(indoc!(
 8974        "fn a() {
 8975             // «dog()ˇ»;
 8976             cat();
 8977        }"
 8978    ));
 8979
 8980    // Multiple cursors on one line -> advance
 8981    cx.set_state(indoc!(
 8982        "fn a() {
 8983             ˇdˇog();
 8984             cat();
 8985        }"
 8986    ));
 8987    cx.update_editor(|editor, cx| {
 8988        editor.toggle_comments(toggle_comments, cx);
 8989    });
 8990    cx.assert_editor_state(indoc!(
 8991        "fn a() {
 8992             // dog();
 8993             catˇ(ˇ);
 8994        }"
 8995    ));
 8996
 8997    // Multiple cursors on one line, with selection -> don't advance
 8998    cx.set_state(indoc!(
 8999        "fn a() {
 9000             ˇdˇog«()ˇ»;
 9001             cat();
 9002        }"
 9003    ));
 9004    cx.update_editor(|editor, cx| {
 9005        editor.toggle_comments(toggle_comments, cx);
 9006    });
 9007    cx.assert_editor_state(indoc!(
 9008        "fn a() {
 9009             // ˇdˇog«()ˇ»;
 9010             cat();
 9011        }"
 9012    ));
 9013
 9014    // Single cursor on one line -> advance
 9015    // Cursor moves to column 0 on blank line
 9016    cx.set_state(indoc!(
 9017        "fn a() {
 9018             ˇdog();
 9019
 9020             cat();
 9021        }"
 9022    ));
 9023    cx.update_editor(|editor, cx| {
 9024        editor.toggle_comments(toggle_comments, cx);
 9025    });
 9026    cx.assert_editor_state(indoc!(
 9027        "fn a() {
 9028             // dog();
 9029        ˇ
 9030             cat();
 9031        }"
 9032    ));
 9033
 9034    // Single cursor on one line -> advance
 9035    // Cursor starts and ends at column 0
 9036    cx.set_state(indoc!(
 9037        "fn a() {
 9038         ˇ    dog();
 9039             cat();
 9040        }"
 9041    ));
 9042    cx.update_editor(|editor, cx| {
 9043        editor.toggle_comments(toggle_comments, cx);
 9044    });
 9045    cx.assert_editor_state(indoc!(
 9046        "fn a() {
 9047             // dog();
 9048         ˇ    cat();
 9049        }"
 9050    ));
 9051}
 9052
 9053#[gpui::test]
 9054async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9055    init_test(cx, |_| {});
 9056
 9057    let mut cx = EditorTestContext::new(cx).await;
 9058
 9059    let html_language = Arc::new(
 9060        Language::new(
 9061            LanguageConfig {
 9062                name: "HTML".into(),
 9063                block_comment: Some(("<!-- ".into(), " -->".into())),
 9064                ..Default::default()
 9065            },
 9066            Some(tree_sitter_html::language()),
 9067        )
 9068        .with_injection_query(
 9069            r#"
 9070            (script_element
 9071                (raw_text) @content
 9072                (#set! "language" "javascript"))
 9073            "#,
 9074        )
 9075        .unwrap(),
 9076    );
 9077
 9078    let javascript_language = Arc::new(Language::new(
 9079        LanguageConfig {
 9080            name: "JavaScript".into(),
 9081            line_comments: vec!["// ".into()],
 9082            ..Default::default()
 9083        },
 9084        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9085    ));
 9086
 9087    cx.language_registry().add(html_language.clone());
 9088    cx.language_registry().add(javascript_language.clone());
 9089    cx.update_buffer(|buffer, cx| {
 9090        buffer.set_language(Some(html_language), cx);
 9091    });
 9092
 9093    // Toggle comments for empty selections
 9094    cx.set_state(
 9095        &r#"
 9096            <p>A</p>ˇ
 9097            <p>B</p>ˇ
 9098            <p>C</p>ˇ
 9099        "#
 9100        .unindent(),
 9101    );
 9102    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9103    cx.assert_editor_state(
 9104        &r#"
 9105            <!-- <p>A</p>ˇ -->
 9106            <!-- <p>B</p>ˇ -->
 9107            <!-- <p>C</p>ˇ -->
 9108        "#
 9109        .unindent(),
 9110    );
 9111    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9112    cx.assert_editor_state(
 9113        &r#"
 9114            <p>A</p>ˇ
 9115            <p>B</p>ˇ
 9116            <p>C</p>ˇ
 9117        "#
 9118        .unindent(),
 9119    );
 9120
 9121    // Toggle comments for mixture of empty and non-empty selections, where
 9122    // multiple selections occupy a given line.
 9123    cx.set_state(
 9124        &r#"
 9125            <p>A«</p>
 9126            <p>ˇ»B</p>ˇ
 9127            <p>C«</p>
 9128            <p>ˇ»D</p>ˇ
 9129        "#
 9130        .unindent(),
 9131    );
 9132
 9133    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9134    cx.assert_editor_state(
 9135        &r#"
 9136            <!-- <p>A«</p>
 9137            <p>ˇ»B</p>ˇ -->
 9138            <!-- <p>C«</p>
 9139            <p>ˇ»D</p>ˇ -->
 9140        "#
 9141        .unindent(),
 9142    );
 9143    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9144    cx.assert_editor_state(
 9145        &r#"
 9146            <p>A«</p>
 9147            <p>ˇ»B</p>ˇ
 9148            <p>C«</p>
 9149            <p>ˇ»D</p>ˇ
 9150        "#
 9151        .unindent(),
 9152    );
 9153
 9154    // Toggle comments when different languages are active for different
 9155    // selections.
 9156    cx.set_state(
 9157        &r#"
 9158            ˇ<script>
 9159                ˇvar x = new Y();
 9160            ˇ</script>
 9161        "#
 9162        .unindent(),
 9163    );
 9164    cx.executor().run_until_parked();
 9165    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9166    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9167    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9168    cx.assert_editor_state(
 9169        &r#"
 9170            <!-- ˇ<script> -->
 9171                // ˇvar x = new Y();
 9172            // ˇ</script>
 9173        "#
 9174        .unindent(),
 9175    );
 9176}
 9177
 9178#[gpui::test]
 9179fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9180    init_test(cx, |_| {});
 9181
 9182    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9183    let multibuffer = cx.new_model(|cx| {
 9184        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9185        multibuffer.push_excerpts(
 9186            buffer.clone(),
 9187            [
 9188                ExcerptRange {
 9189                    context: Point::new(0, 0)..Point::new(0, 4),
 9190                    primary: None,
 9191                },
 9192                ExcerptRange {
 9193                    context: Point::new(1, 0)..Point::new(1, 4),
 9194                    primary: None,
 9195                },
 9196            ],
 9197            cx,
 9198        );
 9199        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9200        multibuffer
 9201    });
 9202
 9203    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9204    view.update(cx, |view, cx| {
 9205        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9206        view.change_selections(None, cx, |s| {
 9207            s.select_ranges([
 9208                Point::new(0, 0)..Point::new(0, 0),
 9209                Point::new(1, 0)..Point::new(1, 0),
 9210            ])
 9211        });
 9212
 9213        view.handle_input("X", cx);
 9214        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9215        assert_eq!(
 9216            view.selections.ranges(cx),
 9217            [
 9218                Point::new(0, 1)..Point::new(0, 1),
 9219                Point::new(1, 1)..Point::new(1, 1),
 9220            ]
 9221        );
 9222
 9223        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9224        view.change_selections(None, cx, |s| {
 9225            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9226        });
 9227        view.backspace(&Default::default(), cx);
 9228        assert_eq!(view.text(cx), "Xa\nbbb");
 9229        assert_eq!(
 9230            view.selections.ranges(cx),
 9231            [Point::new(1, 0)..Point::new(1, 0)]
 9232        );
 9233
 9234        view.change_selections(None, cx, |s| {
 9235            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9236        });
 9237        view.backspace(&Default::default(), cx);
 9238        assert_eq!(view.text(cx), "X\nbb");
 9239        assert_eq!(
 9240            view.selections.ranges(cx),
 9241            [Point::new(0, 1)..Point::new(0, 1)]
 9242        );
 9243    });
 9244}
 9245
 9246#[gpui::test]
 9247fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9248    init_test(cx, |_| {});
 9249
 9250    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9251    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9252        indoc! {"
 9253            [aaaa
 9254            (bbbb]
 9255            cccc)",
 9256        },
 9257        markers.clone(),
 9258    );
 9259    let excerpt_ranges = markers.into_iter().map(|marker| {
 9260        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9261        ExcerptRange {
 9262            context,
 9263            primary: None,
 9264        }
 9265    });
 9266    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9267    let multibuffer = cx.new_model(|cx| {
 9268        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9269        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9270        multibuffer
 9271    });
 9272
 9273    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9274    view.update(cx, |view, cx| {
 9275        let (expected_text, selection_ranges) = marked_text_ranges(
 9276            indoc! {"
 9277                aaaa
 9278                bˇbbb
 9279                bˇbbˇb
 9280                cccc"
 9281            },
 9282            true,
 9283        );
 9284        assert_eq!(view.text(cx), expected_text);
 9285        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9286
 9287        view.handle_input("X", cx);
 9288
 9289        let (expected_text, expected_selections) = marked_text_ranges(
 9290            indoc! {"
 9291                aaaa
 9292                bXˇbbXb
 9293                bXˇbbXˇb
 9294                cccc"
 9295            },
 9296            false,
 9297        );
 9298        assert_eq!(view.text(cx), expected_text);
 9299        assert_eq!(view.selections.ranges(cx), expected_selections);
 9300
 9301        view.newline(&Newline, cx);
 9302        let (expected_text, expected_selections) = marked_text_ranges(
 9303            indoc! {"
 9304                aaaa
 9305                bX
 9306                ˇbbX
 9307                b
 9308                bX
 9309                ˇbbX
 9310                ˇb
 9311                cccc"
 9312            },
 9313            false,
 9314        );
 9315        assert_eq!(view.text(cx), expected_text);
 9316        assert_eq!(view.selections.ranges(cx), expected_selections);
 9317    });
 9318}
 9319
 9320#[gpui::test]
 9321fn test_refresh_selections(cx: &mut TestAppContext) {
 9322    init_test(cx, |_| {});
 9323
 9324    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9325    let mut excerpt1_id = None;
 9326    let multibuffer = cx.new_model(|cx| {
 9327        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9328        excerpt1_id = multibuffer
 9329            .push_excerpts(
 9330                buffer.clone(),
 9331                [
 9332                    ExcerptRange {
 9333                        context: Point::new(0, 0)..Point::new(1, 4),
 9334                        primary: None,
 9335                    },
 9336                    ExcerptRange {
 9337                        context: Point::new(1, 0)..Point::new(2, 4),
 9338                        primary: None,
 9339                    },
 9340                ],
 9341                cx,
 9342            )
 9343            .into_iter()
 9344            .next();
 9345        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9346        multibuffer
 9347    });
 9348
 9349    let editor = cx.add_window(|cx| {
 9350        let mut editor = build_editor(multibuffer.clone(), cx);
 9351        let snapshot = editor.snapshot(cx);
 9352        editor.change_selections(None, cx, |s| {
 9353            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9354        });
 9355        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9356        assert_eq!(
 9357            editor.selections.ranges(cx),
 9358            [
 9359                Point::new(1, 3)..Point::new(1, 3),
 9360                Point::new(2, 1)..Point::new(2, 1),
 9361            ]
 9362        );
 9363        editor
 9364    });
 9365
 9366    // Refreshing selections is a no-op when excerpts haven't changed.
 9367    _ = editor.update(cx, |editor, cx| {
 9368        editor.change_selections(None, cx, |s| s.refresh());
 9369        assert_eq!(
 9370            editor.selections.ranges(cx),
 9371            [
 9372                Point::new(1, 3)..Point::new(1, 3),
 9373                Point::new(2, 1)..Point::new(2, 1),
 9374            ]
 9375        );
 9376    });
 9377
 9378    multibuffer.update(cx, |multibuffer, cx| {
 9379        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9380    });
 9381    _ = editor.update(cx, |editor, cx| {
 9382        // Removing an excerpt causes the first selection to become degenerate.
 9383        assert_eq!(
 9384            editor.selections.ranges(cx),
 9385            [
 9386                Point::new(0, 0)..Point::new(0, 0),
 9387                Point::new(0, 1)..Point::new(0, 1)
 9388            ]
 9389        );
 9390
 9391        // Refreshing selections will relocate the first selection to the original buffer
 9392        // location.
 9393        editor.change_selections(None, cx, |s| s.refresh());
 9394        assert_eq!(
 9395            editor.selections.ranges(cx),
 9396            [
 9397                Point::new(0, 1)..Point::new(0, 1),
 9398                Point::new(0, 3)..Point::new(0, 3)
 9399            ]
 9400        );
 9401        assert!(editor.selections.pending_anchor().is_some());
 9402    });
 9403}
 9404
 9405#[gpui::test]
 9406fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9407    init_test(cx, |_| {});
 9408
 9409    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9410    let mut excerpt1_id = None;
 9411    let multibuffer = cx.new_model(|cx| {
 9412        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9413        excerpt1_id = multibuffer
 9414            .push_excerpts(
 9415                buffer.clone(),
 9416                [
 9417                    ExcerptRange {
 9418                        context: Point::new(0, 0)..Point::new(1, 4),
 9419                        primary: None,
 9420                    },
 9421                    ExcerptRange {
 9422                        context: Point::new(1, 0)..Point::new(2, 4),
 9423                        primary: None,
 9424                    },
 9425                ],
 9426                cx,
 9427            )
 9428            .into_iter()
 9429            .next();
 9430        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9431        multibuffer
 9432    });
 9433
 9434    let editor = cx.add_window(|cx| {
 9435        let mut editor = build_editor(multibuffer.clone(), cx);
 9436        let snapshot = editor.snapshot(cx);
 9437        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9438        assert_eq!(
 9439            editor.selections.ranges(cx),
 9440            [Point::new(1, 3)..Point::new(1, 3)]
 9441        );
 9442        editor
 9443    });
 9444
 9445    multibuffer.update(cx, |multibuffer, cx| {
 9446        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9447    });
 9448    _ = editor.update(cx, |editor, cx| {
 9449        assert_eq!(
 9450            editor.selections.ranges(cx),
 9451            [Point::new(0, 0)..Point::new(0, 0)]
 9452        );
 9453
 9454        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9455        editor.change_selections(None, cx, |s| s.refresh());
 9456        assert_eq!(
 9457            editor.selections.ranges(cx),
 9458            [Point::new(0, 3)..Point::new(0, 3)]
 9459        );
 9460        assert!(editor.selections.pending_anchor().is_some());
 9461    });
 9462}
 9463
 9464#[gpui::test]
 9465async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9466    init_test(cx, |_| {});
 9467
 9468    let language = Arc::new(
 9469        Language::new(
 9470            LanguageConfig {
 9471                brackets: BracketPairConfig {
 9472                    pairs: vec![
 9473                        BracketPair {
 9474                            start: "{".to_string(),
 9475                            end: "}".to_string(),
 9476                            close: true,
 9477                            surround: true,
 9478                            newline: true,
 9479                        },
 9480                        BracketPair {
 9481                            start: "/* ".to_string(),
 9482                            end: " */".to_string(),
 9483                            close: true,
 9484                            surround: true,
 9485                            newline: true,
 9486                        },
 9487                    ],
 9488                    ..Default::default()
 9489                },
 9490                ..Default::default()
 9491            },
 9492            Some(tree_sitter_rust::LANGUAGE.into()),
 9493        )
 9494        .with_indents_query("")
 9495        .unwrap(),
 9496    );
 9497
 9498    let text = concat!(
 9499        "{   }\n",     //
 9500        "  x\n",       //
 9501        "  /*   */\n", //
 9502        "x\n",         //
 9503        "{{} }\n",     //
 9504    );
 9505
 9506    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9507    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9508    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9509    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9510        .await;
 9511
 9512    view.update(cx, |view, cx| {
 9513        view.change_selections(None, cx, |s| {
 9514            s.select_display_ranges([
 9515                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9516                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9517                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9518            ])
 9519        });
 9520        view.newline(&Newline, cx);
 9521
 9522        assert_eq!(
 9523            view.buffer().read(cx).read(cx).text(),
 9524            concat!(
 9525                "{ \n",    // Suppress rustfmt
 9526                "\n",      //
 9527                "}\n",     //
 9528                "  x\n",   //
 9529                "  /* \n", //
 9530                "  \n",    //
 9531                "  */\n",  //
 9532                "x\n",     //
 9533                "{{} \n",  //
 9534                "}\n",     //
 9535            )
 9536        );
 9537    });
 9538}
 9539
 9540#[gpui::test]
 9541fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9542    init_test(cx, |_| {});
 9543
 9544    let editor = cx.add_window(|cx| {
 9545        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9546        build_editor(buffer.clone(), cx)
 9547    });
 9548
 9549    _ = editor.update(cx, |editor, cx| {
 9550        struct Type1;
 9551        struct Type2;
 9552
 9553        let buffer = editor.buffer.read(cx).snapshot(cx);
 9554
 9555        let anchor_range =
 9556            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9557
 9558        editor.highlight_background::<Type1>(
 9559            &[
 9560                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9561                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9562                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9563                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9564            ],
 9565            |_| Hsla::red(),
 9566            cx,
 9567        );
 9568        editor.highlight_background::<Type2>(
 9569            &[
 9570                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9571                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9572                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9573                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9574            ],
 9575            |_| Hsla::green(),
 9576            cx,
 9577        );
 9578
 9579        let snapshot = editor.snapshot(cx);
 9580        let mut highlighted_ranges = editor.background_highlights_in_range(
 9581            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9582            &snapshot,
 9583            cx.theme().colors(),
 9584        );
 9585        // Enforce a consistent ordering based on color without relying on the ordering of the
 9586        // highlight's `TypeId` which is non-executor.
 9587        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9588        assert_eq!(
 9589            highlighted_ranges,
 9590            &[
 9591                (
 9592                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9593                    Hsla::red(),
 9594                ),
 9595                (
 9596                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9597                    Hsla::red(),
 9598                ),
 9599                (
 9600                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9601                    Hsla::green(),
 9602                ),
 9603                (
 9604                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9605                    Hsla::green(),
 9606                ),
 9607            ]
 9608        );
 9609        assert_eq!(
 9610            editor.background_highlights_in_range(
 9611                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9612                &snapshot,
 9613                cx.theme().colors(),
 9614            ),
 9615            &[(
 9616                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9617                Hsla::red(),
 9618            )]
 9619        );
 9620    });
 9621}
 9622
 9623#[gpui::test]
 9624async fn test_following(cx: &mut gpui::TestAppContext) {
 9625    init_test(cx, |_| {});
 9626
 9627    let fs = FakeFs::new(cx.executor());
 9628    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9629
 9630    let buffer = project.update(cx, |project, cx| {
 9631        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9632        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9633    });
 9634    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9635    let follower = cx.update(|cx| {
 9636        cx.open_window(
 9637            WindowOptions {
 9638                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9639                    gpui::Point::new(px(0.), px(0.)),
 9640                    gpui::Point::new(px(10.), px(80.)),
 9641                ))),
 9642                ..Default::default()
 9643            },
 9644            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9645        )
 9646        .unwrap()
 9647    });
 9648
 9649    let is_still_following = Rc::new(RefCell::new(true));
 9650    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9651    let pending_update = Rc::new(RefCell::new(None));
 9652    _ = follower.update(cx, {
 9653        let update = pending_update.clone();
 9654        let is_still_following = is_still_following.clone();
 9655        let follower_edit_event_count = follower_edit_event_count.clone();
 9656        |_, cx| {
 9657            cx.subscribe(
 9658                &leader.root_view(cx).unwrap(),
 9659                move |_, leader, event, cx| {
 9660                    leader
 9661                        .read(cx)
 9662                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9663                },
 9664            )
 9665            .detach();
 9666
 9667            cx.subscribe(
 9668                &follower.root_view(cx).unwrap(),
 9669                move |_, _, event: &EditorEvent, _cx| {
 9670                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9671                        *is_still_following.borrow_mut() = false;
 9672                    }
 9673
 9674                    if let EditorEvent::BufferEdited = event {
 9675                        *follower_edit_event_count.borrow_mut() += 1;
 9676                    }
 9677                },
 9678            )
 9679            .detach();
 9680        }
 9681    });
 9682
 9683    // Update the selections only
 9684    _ = leader.update(cx, |leader, cx| {
 9685        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9686    });
 9687    follower
 9688        .update(cx, |follower, cx| {
 9689            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9690        })
 9691        .unwrap()
 9692        .await
 9693        .unwrap();
 9694    _ = follower.update(cx, |follower, cx| {
 9695        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9696    });
 9697    assert!(*is_still_following.borrow());
 9698    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9699
 9700    // Update the scroll position only
 9701    _ = leader.update(cx, |leader, cx| {
 9702        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9703    });
 9704    follower
 9705        .update(cx, |follower, cx| {
 9706            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9707        })
 9708        .unwrap()
 9709        .await
 9710        .unwrap();
 9711    assert_eq!(
 9712        follower
 9713            .update(cx, |follower, cx| follower.scroll_position(cx))
 9714            .unwrap(),
 9715        gpui::Point::new(1.5, 3.5)
 9716    );
 9717    assert!(*is_still_following.borrow());
 9718    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9719
 9720    // Update the selections and scroll position. The follower's scroll position is updated
 9721    // via autoscroll, not via the leader's exact scroll position.
 9722    _ = leader.update(cx, |leader, cx| {
 9723        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9724        leader.request_autoscroll(Autoscroll::newest(), cx);
 9725        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9726    });
 9727    follower
 9728        .update(cx, |follower, cx| {
 9729            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9730        })
 9731        .unwrap()
 9732        .await
 9733        .unwrap();
 9734    _ = follower.update(cx, |follower, cx| {
 9735        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9736        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9737    });
 9738    assert!(*is_still_following.borrow());
 9739
 9740    // Creating a pending selection that precedes another selection
 9741    _ = leader.update(cx, |leader, cx| {
 9742        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9743        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9744    });
 9745    follower
 9746        .update(cx, |follower, cx| {
 9747            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9748        })
 9749        .unwrap()
 9750        .await
 9751        .unwrap();
 9752    _ = follower.update(cx, |follower, cx| {
 9753        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9754    });
 9755    assert!(*is_still_following.borrow());
 9756
 9757    // Extend the pending selection so that it surrounds another selection
 9758    _ = leader.update(cx, |leader, cx| {
 9759        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9760    });
 9761    follower
 9762        .update(cx, |follower, cx| {
 9763            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9764        })
 9765        .unwrap()
 9766        .await
 9767        .unwrap();
 9768    _ = follower.update(cx, |follower, cx| {
 9769        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9770    });
 9771
 9772    // Scrolling locally breaks the follow
 9773    _ = follower.update(cx, |follower, cx| {
 9774        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9775        follower.set_scroll_anchor(
 9776            ScrollAnchor {
 9777                anchor: top_anchor,
 9778                offset: gpui::Point::new(0.0, 0.5),
 9779            },
 9780            cx,
 9781        );
 9782    });
 9783    assert!(!(*is_still_following.borrow()));
 9784}
 9785
 9786#[gpui::test]
 9787async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9788    init_test(cx, |_| {});
 9789
 9790    let fs = FakeFs::new(cx.executor());
 9791    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9792    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9793    let pane = workspace
 9794        .update(cx, |workspace, _| workspace.active_pane().clone())
 9795        .unwrap();
 9796
 9797    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9798
 9799    let leader = pane.update(cx, |_, cx| {
 9800        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9801        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9802    });
 9803
 9804    // Start following the editor when it has no excerpts.
 9805    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9806    let follower_1 = cx
 9807        .update_window(*workspace.deref(), |_, cx| {
 9808            Editor::from_state_proto(
 9809                workspace.root_view(cx).unwrap(),
 9810                ViewId {
 9811                    creator: Default::default(),
 9812                    id: 0,
 9813                },
 9814                &mut state_message,
 9815                cx,
 9816            )
 9817        })
 9818        .unwrap()
 9819        .unwrap()
 9820        .await
 9821        .unwrap();
 9822
 9823    let update_message = Rc::new(RefCell::new(None));
 9824    follower_1.update(cx, {
 9825        let update = update_message.clone();
 9826        |_, cx| {
 9827            cx.subscribe(&leader, move |_, leader, event, cx| {
 9828                leader
 9829                    .read(cx)
 9830                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9831            })
 9832            .detach();
 9833        }
 9834    });
 9835
 9836    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9837        (
 9838            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9839            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9840        )
 9841    });
 9842
 9843    // Insert some excerpts.
 9844    leader.update(cx, |leader, cx| {
 9845        leader.buffer.update(cx, |multibuffer, cx| {
 9846            let excerpt_ids = multibuffer.push_excerpts(
 9847                buffer_1.clone(),
 9848                [
 9849                    ExcerptRange {
 9850                        context: 1..6,
 9851                        primary: None,
 9852                    },
 9853                    ExcerptRange {
 9854                        context: 12..15,
 9855                        primary: None,
 9856                    },
 9857                    ExcerptRange {
 9858                        context: 0..3,
 9859                        primary: None,
 9860                    },
 9861                ],
 9862                cx,
 9863            );
 9864            multibuffer.insert_excerpts_after(
 9865                excerpt_ids[0],
 9866                buffer_2.clone(),
 9867                [
 9868                    ExcerptRange {
 9869                        context: 8..12,
 9870                        primary: None,
 9871                    },
 9872                    ExcerptRange {
 9873                        context: 0..6,
 9874                        primary: None,
 9875                    },
 9876                ],
 9877                cx,
 9878            );
 9879        });
 9880    });
 9881
 9882    // Apply the update of adding the excerpts.
 9883    follower_1
 9884        .update(cx, |follower, cx| {
 9885            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9886        })
 9887        .await
 9888        .unwrap();
 9889    assert_eq!(
 9890        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9891        leader.update(cx, |editor, cx| editor.text(cx))
 9892    );
 9893    update_message.borrow_mut().take();
 9894
 9895    // Start following separately after it already has excerpts.
 9896    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9897    let follower_2 = cx
 9898        .update_window(*workspace.deref(), |_, cx| {
 9899            Editor::from_state_proto(
 9900                workspace.root_view(cx).unwrap().clone(),
 9901                ViewId {
 9902                    creator: Default::default(),
 9903                    id: 0,
 9904                },
 9905                &mut state_message,
 9906                cx,
 9907            )
 9908        })
 9909        .unwrap()
 9910        .unwrap()
 9911        .await
 9912        .unwrap();
 9913    assert_eq!(
 9914        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9915        leader.update(cx, |editor, cx| editor.text(cx))
 9916    );
 9917
 9918    // Remove some excerpts.
 9919    leader.update(cx, |leader, cx| {
 9920        leader.buffer.update(cx, |multibuffer, cx| {
 9921            let excerpt_ids = multibuffer.excerpt_ids();
 9922            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9923            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9924        });
 9925    });
 9926
 9927    // Apply the update of removing the excerpts.
 9928    follower_1
 9929        .update(cx, |follower, cx| {
 9930            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9931        })
 9932        .await
 9933        .unwrap();
 9934    follower_2
 9935        .update(cx, |follower, cx| {
 9936            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9937        })
 9938        .await
 9939        .unwrap();
 9940    update_message.borrow_mut().take();
 9941    assert_eq!(
 9942        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9943        leader.update(cx, |editor, cx| editor.text(cx))
 9944    );
 9945}
 9946
 9947#[gpui::test]
 9948async fn go_to_prev_overlapping_diagnostic(
 9949    executor: BackgroundExecutor,
 9950    cx: &mut gpui::TestAppContext,
 9951) {
 9952    init_test(cx, |_| {});
 9953
 9954    let mut cx = EditorTestContext::new(cx).await;
 9955    let lsp_store =
 9956        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
 9957
 9958    cx.set_state(indoc! {"
 9959        ˇfn func(abc def: i32) -> u32 {
 9960        }
 9961    "});
 9962
 9963    cx.update(|cx| {
 9964        lsp_store.update(cx, |lsp_store, cx| {
 9965            lsp_store
 9966                .update_diagnostics(
 9967                    LanguageServerId(0),
 9968                    lsp::PublishDiagnosticsParams {
 9969                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9970                        version: None,
 9971                        diagnostics: vec![
 9972                            lsp::Diagnostic {
 9973                                range: lsp::Range::new(
 9974                                    lsp::Position::new(0, 11),
 9975                                    lsp::Position::new(0, 12),
 9976                                ),
 9977                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9978                                ..Default::default()
 9979                            },
 9980                            lsp::Diagnostic {
 9981                                range: lsp::Range::new(
 9982                                    lsp::Position::new(0, 12),
 9983                                    lsp::Position::new(0, 15),
 9984                                ),
 9985                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9986                                ..Default::default()
 9987                            },
 9988                            lsp::Diagnostic {
 9989                                range: lsp::Range::new(
 9990                                    lsp::Position::new(0, 25),
 9991                                    lsp::Position::new(0, 28),
 9992                                ),
 9993                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9994                                ..Default::default()
 9995                            },
 9996                        ],
 9997                    },
 9998                    &[],
 9999                    cx,
10000                )
10001                .unwrap()
10002        });
10003    });
10004
10005    executor.run_until_parked();
10006
10007    cx.update_editor(|editor, cx| {
10008        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10009    });
10010
10011    cx.assert_editor_state(indoc! {"
10012        fn func(abc def: i32) -> ˇu32 {
10013        }
10014    "});
10015
10016    cx.update_editor(|editor, cx| {
10017        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10018    });
10019
10020    cx.assert_editor_state(indoc! {"
10021        fn func(abc ˇdef: i32) -> u32 {
10022        }
10023    "});
10024
10025    cx.update_editor(|editor, cx| {
10026        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10027    });
10028
10029    cx.assert_editor_state(indoc! {"
10030        fn func(abcˇ def: i32) -> u32 {
10031        }
10032    "});
10033
10034    cx.update_editor(|editor, cx| {
10035        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10036    });
10037
10038    cx.assert_editor_state(indoc! {"
10039        fn func(abc def: i32) -> ˇu32 {
10040        }
10041    "});
10042}
10043
10044#[gpui::test]
10045async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10046    init_test(cx, |_| {});
10047
10048    let mut cx = EditorTestContext::new(cx).await;
10049
10050    cx.set_state(indoc! {"
10051        fn func(abˇc def: i32) -> u32 {
10052        }
10053    "});
10054    let lsp_store =
10055        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10056
10057    cx.update(|cx| {
10058        lsp_store.update(cx, |lsp_store, cx| {
10059            lsp_store.update_diagnostics(
10060                LanguageServerId(0),
10061                lsp::PublishDiagnosticsParams {
10062                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10063                    version: None,
10064                    diagnostics: vec![lsp::Diagnostic {
10065                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10066                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10067                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10068                        ..Default::default()
10069                    }],
10070                },
10071                &[],
10072                cx,
10073            )
10074        })
10075    }).unwrap();
10076    cx.run_until_parked();
10077    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10078    cx.run_until_parked();
10079    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10080}
10081
10082#[gpui::test]
10083async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10084    init_test(cx, |_| {});
10085
10086    let mut cx = EditorTestContext::new(cx).await;
10087
10088    let diff_base = r#"
10089        use some::mod;
10090
10091        const A: u32 = 42;
10092
10093        fn main() {
10094            println!("hello");
10095
10096            println!("world");
10097        }
10098        "#
10099    .unindent();
10100
10101    // Edits are modified, removed, modified, added
10102    cx.set_state(
10103        &r#"
10104        use some::modified;
10105
10106        ˇ
10107        fn main() {
10108            println!("hello there");
10109
10110            println!("around the");
10111            println!("world");
10112        }
10113        "#
10114        .unindent(),
10115    );
10116
10117    cx.set_diff_base(&diff_base);
10118    executor.run_until_parked();
10119
10120    cx.update_editor(|editor, cx| {
10121        //Wrap around the bottom of the buffer
10122        for _ in 0..3 {
10123            editor.go_to_next_hunk(&GoToHunk, cx);
10124        }
10125    });
10126
10127    cx.assert_editor_state(
10128        &r#"
10129        ˇuse some::modified;
10130
10131
10132        fn main() {
10133            println!("hello there");
10134
10135            println!("around the");
10136            println!("world");
10137        }
10138        "#
10139        .unindent(),
10140    );
10141
10142    cx.update_editor(|editor, cx| {
10143        //Wrap around the top of the buffer
10144        for _ in 0..2 {
10145            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10146        }
10147    });
10148
10149    cx.assert_editor_state(
10150        &r#"
10151        use some::modified;
10152
10153
10154        fn main() {
10155        ˇ    println!("hello there");
10156
10157            println!("around the");
10158            println!("world");
10159        }
10160        "#
10161        .unindent(),
10162    );
10163
10164    cx.update_editor(|editor, cx| {
10165        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10166    });
10167
10168    cx.assert_editor_state(
10169        &r#"
10170        use some::modified;
10171
10172        ˇ
10173        fn main() {
10174            println!("hello there");
10175
10176            println!("around the");
10177            println!("world");
10178        }
10179        "#
10180        .unindent(),
10181    );
10182
10183    cx.update_editor(|editor, cx| {
10184        for _ in 0..3 {
10185            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10186        }
10187    });
10188
10189    cx.assert_editor_state(
10190        &r#"
10191        use some::modified;
10192
10193
10194        fn main() {
10195        ˇ    println!("hello there");
10196
10197            println!("around the");
10198            println!("world");
10199        }
10200        "#
10201        .unindent(),
10202    );
10203
10204    cx.update_editor(|editor, cx| {
10205        editor.fold(&Fold, cx);
10206
10207        //Make sure that the fold only gets one hunk
10208        for _ in 0..4 {
10209            editor.go_to_next_hunk(&GoToHunk, cx);
10210        }
10211    });
10212
10213    cx.assert_editor_state(
10214        &r#"
10215        ˇuse some::modified;
10216
10217
10218        fn main() {
10219            println!("hello there");
10220
10221            println!("around the");
10222            println!("world");
10223        }
10224        "#
10225        .unindent(),
10226    );
10227}
10228
10229#[test]
10230fn test_split_words() {
10231    fn split(text: &str) -> Vec<&str> {
10232        split_words(text).collect()
10233    }
10234
10235    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10236    assert_eq!(split("hello_world"), &["hello_", "world"]);
10237    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10238    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10239    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10240    assert_eq!(split("helloworld"), &["helloworld"]);
10241
10242    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10243}
10244
10245#[gpui::test]
10246async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10247    init_test(cx, |_| {});
10248
10249    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10250    let mut assert = |before, after| {
10251        let _state_context = cx.set_state(before);
10252        cx.update_editor(|editor, cx| {
10253            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10254        });
10255        cx.assert_editor_state(after);
10256    };
10257
10258    // Outside bracket jumps to outside of matching bracket
10259    assert("console.logˇ(var);", "console.log(var)ˇ;");
10260    assert("console.log(var)ˇ;", "console.logˇ(var);");
10261
10262    // Inside bracket jumps to inside of matching bracket
10263    assert("console.log(ˇvar);", "console.log(varˇ);");
10264    assert("console.log(varˇ);", "console.log(ˇvar);");
10265
10266    // When outside a bracket and inside, favor jumping to the inside bracket
10267    assert(
10268        "console.log('foo', [1, 2, 3]ˇ);",
10269        "console.log(ˇ'foo', [1, 2, 3]);",
10270    );
10271    assert(
10272        "console.log(ˇ'foo', [1, 2, 3]);",
10273        "console.log('foo', [1, 2, 3]ˇ);",
10274    );
10275
10276    // Bias forward if two options are equally likely
10277    assert(
10278        "let result = curried_fun()ˇ();",
10279        "let result = curried_fun()()ˇ;",
10280    );
10281
10282    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10283    assert(
10284        indoc! {"
10285            function test() {
10286                console.log('test')ˇ
10287            }"},
10288        indoc! {"
10289            function test() {
10290                console.logˇ('test')
10291            }"},
10292    );
10293}
10294
10295#[gpui::test]
10296async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10297    init_test(cx, |_| {});
10298
10299    let fs = FakeFs::new(cx.executor());
10300    fs.insert_tree(
10301        "/a",
10302        json!({
10303            "main.rs": "fn main() { let a = 5; }",
10304            "other.rs": "// Test file",
10305        }),
10306    )
10307    .await;
10308    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10309
10310    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10311    language_registry.add(Arc::new(Language::new(
10312        LanguageConfig {
10313            name: "Rust".into(),
10314            matcher: LanguageMatcher {
10315                path_suffixes: vec!["rs".to_string()],
10316                ..Default::default()
10317            },
10318            brackets: BracketPairConfig {
10319                pairs: vec![BracketPair {
10320                    start: "{".to_string(),
10321                    end: "}".to_string(),
10322                    close: true,
10323                    surround: true,
10324                    newline: true,
10325                }],
10326                disabled_scopes_by_bracket_ix: Vec::new(),
10327            },
10328            ..Default::default()
10329        },
10330        Some(tree_sitter_rust::LANGUAGE.into()),
10331    )));
10332    let mut fake_servers = language_registry.register_fake_lsp(
10333        "Rust",
10334        FakeLspAdapter {
10335            capabilities: lsp::ServerCapabilities {
10336                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10337                    first_trigger_character: "{".to_string(),
10338                    more_trigger_character: None,
10339                }),
10340                ..Default::default()
10341            },
10342            ..Default::default()
10343        },
10344    );
10345
10346    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10347
10348    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10349
10350    let worktree_id = workspace
10351        .update(cx, |workspace, cx| {
10352            workspace.project().update(cx, |project, cx| {
10353                project.worktrees(cx).next().unwrap().read(cx).id()
10354            })
10355        })
10356        .unwrap();
10357
10358    let buffer = project
10359        .update(cx, |project, cx| {
10360            project.open_local_buffer("/a/main.rs", cx)
10361        })
10362        .await
10363        .unwrap();
10364    let editor_handle = workspace
10365        .update(cx, |workspace, cx| {
10366            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10367        })
10368        .unwrap()
10369        .await
10370        .unwrap()
10371        .downcast::<Editor>()
10372        .unwrap();
10373
10374    cx.executor().start_waiting();
10375    let fake_server = fake_servers.next().await.unwrap();
10376
10377    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10378        assert_eq!(
10379            params.text_document_position.text_document.uri,
10380            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10381        );
10382        assert_eq!(
10383            params.text_document_position.position,
10384            lsp::Position::new(0, 21),
10385        );
10386
10387        Ok(Some(vec![lsp::TextEdit {
10388            new_text: "]".to_string(),
10389            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10390        }]))
10391    });
10392
10393    editor_handle.update(cx, |editor, cx| {
10394        editor.focus(cx);
10395        editor.change_selections(None, cx, |s| {
10396            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10397        });
10398        editor.handle_input("{", cx);
10399    });
10400
10401    cx.executor().run_until_parked();
10402
10403    buffer.update(cx, |buffer, _| {
10404        assert_eq!(
10405            buffer.text(),
10406            "fn main() { let a = {5}; }",
10407            "No extra braces from on type formatting should appear in the buffer"
10408        )
10409    });
10410}
10411
10412#[gpui::test]
10413async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10414    init_test(cx, |_| {});
10415
10416    let fs = FakeFs::new(cx.executor());
10417    fs.insert_tree(
10418        "/a",
10419        json!({
10420            "main.rs": "fn main() { let a = 5; }",
10421            "other.rs": "// Test file",
10422        }),
10423    )
10424    .await;
10425
10426    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10427
10428    let server_restarts = Arc::new(AtomicUsize::new(0));
10429    let closure_restarts = Arc::clone(&server_restarts);
10430    let language_server_name = "test language server";
10431    let language_name: LanguageName = "Rust".into();
10432
10433    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10434    language_registry.add(Arc::new(Language::new(
10435        LanguageConfig {
10436            name: language_name.clone(),
10437            matcher: LanguageMatcher {
10438                path_suffixes: vec!["rs".to_string()],
10439                ..Default::default()
10440            },
10441            ..Default::default()
10442        },
10443        Some(tree_sitter_rust::LANGUAGE.into()),
10444    )));
10445    let mut fake_servers = language_registry.register_fake_lsp(
10446        "Rust",
10447        FakeLspAdapter {
10448            name: language_server_name,
10449            initialization_options: Some(json!({
10450                "testOptionValue": true
10451            })),
10452            initializer: Some(Box::new(move |fake_server| {
10453                let task_restarts = Arc::clone(&closure_restarts);
10454                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10455                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10456                    futures::future::ready(Ok(()))
10457                });
10458            })),
10459            ..Default::default()
10460        },
10461    );
10462
10463    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10464    let _buffer = project
10465        .update(cx, |project, cx| {
10466            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10467        })
10468        .await
10469        .unwrap();
10470    let _fake_server = fake_servers.next().await.unwrap();
10471    update_test_language_settings(cx, |language_settings| {
10472        language_settings.languages.insert(
10473            language_name.clone(),
10474            LanguageSettingsContent {
10475                tab_size: NonZeroU32::new(8),
10476                ..Default::default()
10477            },
10478        );
10479    });
10480    cx.executor().run_until_parked();
10481    assert_eq!(
10482        server_restarts.load(atomic::Ordering::Acquire),
10483        0,
10484        "Should not restart LSP server on an unrelated change"
10485    );
10486
10487    update_test_project_settings(cx, |project_settings| {
10488        project_settings.lsp.insert(
10489            "Some other server name".into(),
10490            LspSettings {
10491                binary: None,
10492                settings: None,
10493                initialization_options: Some(json!({
10494                    "some other init value": false
10495                })),
10496            },
10497        );
10498    });
10499    cx.executor().run_until_parked();
10500    assert_eq!(
10501        server_restarts.load(atomic::Ordering::Acquire),
10502        0,
10503        "Should not restart LSP server on an unrelated LSP settings change"
10504    );
10505
10506    update_test_project_settings(cx, |project_settings| {
10507        project_settings.lsp.insert(
10508            language_server_name.into(),
10509            LspSettings {
10510                binary: None,
10511                settings: None,
10512                initialization_options: Some(json!({
10513                    "anotherInitValue": false
10514                })),
10515            },
10516        );
10517    });
10518    cx.executor().run_until_parked();
10519    assert_eq!(
10520        server_restarts.load(atomic::Ordering::Acquire),
10521        1,
10522        "Should restart LSP server on a related LSP settings change"
10523    );
10524
10525    update_test_project_settings(cx, |project_settings| {
10526        project_settings.lsp.insert(
10527            language_server_name.into(),
10528            LspSettings {
10529                binary: None,
10530                settings: None,
10531                initialization_options: Some(json!({
10532                    "anotherInitValue": false
10533                })),
10534            },
10535        );
10536    });
10537    cx.executor().run_until_parked();
10538    assert_eq!(
10539        server_restarts.load(atomic::Ordering::Acquire),
10540        1,
10541        "Should not restart LSP server on a related LSP settings change that is the same"
10542    );
10543
10544    update_test_project_settings(cx, |project_settings| {
10545        project_settings.lsp.insert(
10546            language_server_name.into(),
10547            LspSettings {
10548                binary: None,
10549                settings: None,
10550                initialization_options: None,
10551            },
10552        );
10553    });
10554    cx.executor().run_until_parked();
10555    assert_eq!(
10556        server_restarts.load(atomic::Ordering::Acquire),
10557        2,
10558        "Should restart LSP server on another related LSP settings change"
10559    );
10560}
10561
10562#[gpui::test]
10563async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10564    init_test(cx, |_| {});
10565
10566    let mut cx = EditorLspTestContext::new_rust(
10567        lsp::ServerCapabilities {
10568            completion_provider: Some(lsp::CompletionOptions {
10569                trigger_characters: Some(vec![".".to_string()]),
10570                resolve_provider: Some(true),
10571                ..Default::default()
10572            }),
10573            ..Default::default()
10574        },
10575        cx,
10576    )
10577    .await;
10578
10579    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10580    cx.simulate_keystroke(".");
10581    let completion_item = lsp::CompletionItem {
10582        label: "some".into(),
10583        kind: Some(lsp::CompletionItemKind::SNIPPET),
10584        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10585        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10586            kind: lsp::MarkupKind::Markdown,
10587            value: "```rust\nSome(2)\n```".to_string(),
10588        })),
10589        deprecated: Some(false),
10590        sort_text: Some("fffffff2".to_string()),
10591        filter_text: Some("some".to_string()),
10592        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10593        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10594            range: lsp::Range {
10595                start: lsp::Position {
10596                    line: 0,
10597                    character: 22,
10598                },
10599                end: lsp::Position {
10600                    line: 0,
10601                    character: 22,
10602                },
10603            },
10604            new_text: "Some(2)".to_string(),
10605        })),
10606        additional_text_edits: Some(vec![lsp::TextEdit {
10607            range: lsp::Range {
10608                start: lsp::Position {
10609                    line: 0,
10610                    character: 20,
10611                },
10612                end: lsp::Position {
10613                    line: 0,
10614                    character: 22,
10615                },
10616            },
10617            new_text: "".to_string(),
10618        }]),
10619        ..Default::default()
10620    };
10621
10622    let closure_completion_item = completion_item.clone();
10623    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10624        let task_completion_item = closure_completion_item.clone();
10625        async move {
10626            Ok(Some(lsp::CompletionResponse::Array(vec![
10627                task_completion_item,
10628            ])))
10629        }
10630    });
10631
10632    request.next().await;
10633
10634    cx.condition(|editor, _| editor.context_menu_visible())
10635        .await;
10636    let apply_additional_edits = cx.update_editor(|editor, cx| {
10637        editor
10638            .confirm_completion(&ConfirmCompletion::default(), cx)
10639            .unwrap()
10640    });
10641    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10642
10643    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10644        let task_completion_item = completion_item.clone();
10645        async move { Ok(task_completion_item) }
10646    })
10647    .next()
10648    .await
10649    .unwrap();
10650    apply_additional_edits.await.unwrap();
10651    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10652}
10653
10654#[gpui::test]
10655async fn test_completions_resolve_updates_labels_if_filter_text_matches(
10656    cx: &mut gpui::TestAppContext,
10657) {
10658    init_test(cx, |_| {});
10659
10660    let mut cx = EditorLspTestContext::new_rust(
10661        lsp::ServerCapabilities {
10662            completion_provider: Some(lsp::CompletionOptions {
10663                trigger_characters: Some(vec![".".to_string()]),
10664                resolve_provider: Some(true),
10665                ..Default::default()
10666            }),
10667            ..Default::default()
10668        },
10669        cx,
10670    )
10671    .await;
10672
10673    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10674    cx.simulate_keystroke(".");
10675
10676    let item1 = lsp::CompletionItem {
10677        label: "id".to_string(),
10678        filter_text: Some("id".to_string()),
10679        detail: None,
10680        documentation: None,
10681        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10682            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10683            new_text: ".id".to_string(),
10684        })),
10685        ..lsp::CompletionItem::default()
10686    };
10687
10688    let item2 = lsp::CompletionItem {
10689        label: "other".to_string(),
10690        filter_text: Some("other".to_string()),
10691        detail: None,
10692        documentation: None,
10693        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10694            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10695            new_text: ".other".to_string(),
10696        })),
10697        ..lsp::CompletionItem::default()
10698    };
10699
10700    let item1 = item1.clone();
10701    cx.handle_request::<lsp::request::Completion, _, _>({
10702        let item1 = item1.clone();
10703        move |_, _, _| {
10704            let item1 = item1.clone();
10705            let item2 = item2.clone();
10706            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
10707        }
10708    })
10709    .next()
10710    .await;
10711
10712    cx.condition(|editor, _| editor.context_menu_visible())
10713        .await;
10714    cx.update_editor(|editor, _| {
10715        let context_menu = editor.context_menu.borrow_mut();
10716        let context_menu = context_menu
10717            .as_ref()
10718            .expect("Should have the context menu deployed");
10719        match context_menu {
10720            CodeContextMenu::Completions(completions_menu) => {
10721                let completions = completions_menu.completions.borrow_mut();
10722                assert_eq!(
10723                    completions
10724                        .iter()
10725                        .map(|completion| &completion.label.text)
10726                        .collect::<Vec<_>>(),
10727                    vec!["id", "other"]
10728                )
10729            }
10730            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10731        }
10732    });
10733
10734    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
10735        let item1 = item1.clone();
10736        move |_, item_to_resolve, _| {
10737            let item1 = item1.clone();
10738            async move {
10739                if item1 == item_to_resolve {
10740                    Ok(lsp::CompletionItem {
10741                        label: "method id()".to_string(),
10742                        filter_text: Some("id".to_string()),
10743                        detail: Some("Now resolved!".to_string()),
10744                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
10745                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10746                            range: lsp::Range::new(
10747                                lsp::Position::new(0, 22),
10748                                lsp::Position::new(0, 22),
10749                            ),
10750                            new_text: ".id".to_string(),
10751                        })),
10752                        ..lsp::CompletionItem::default()
10753                    })
10754                } else {
10755                    Ok(item_to_resolve)
10756                }
10757            }
10758        }
10759    })
10760    .next()
10761    .await
10762    .unwrap();
10763    cx.run_until_parked();
10764
10765    cx.update_editor(|editor, cx| {
10766        editor.context_menu_next(&Default::default(), cx);
10767    });
10768
10769    cx.update_editor(|editor, _| {
10770        let context_menu = editor.context_menu.borrow_mut();
10771        let context_menu = context_menu
10772            .as_ref()
10773            .expect("Should have the context menu deployed");
10774        match context_menu {
10775            CodeContextMenu::Completions(completions_menu) => {
10776                let completions = completions_menu.completions.borrow_mut();
10777                assert_eq!(
10778                    completions
10779                        .iter()
10780                        .map(|completion| &completion.label.text)
10781                        .collect::<Vec<_>>(),
10782                    vec!["method id()", "other"],
10783                    "Should update first completion label, but not second as the filter text did not match."
10784                );
10785            }
10786            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10787        }
10788    });
10789}
10790
10791#[gpui::test]
10792async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
10793    init_test(cx, |_| {});
10794
10795    let mut cx = EditorLspTestContext::new_rust(
10796        lsp::ServerCapabilities {
10797            completion_provider: Some(lsp::CompletionOptions {
10798                trigger_characters: Some(vec![".".to_string()]),
10799                resolve_provider: Some(true),
10800                ..Default::default()
10801            }),
10802            ..Default::default()
10803        },
10804        cx,
10805    )
10806    .await;
10807
10808    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10809    cx.simulate_keystroke(".");
10810
10811    let unresolved_item_1 = lsp::CompletionItem {
10812        label: "id".to_string(),
10813        filter_text: Some("id".to_string()),
10814        detail: None,
10815        documentation: None,
10816        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10817            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10818            new_text: ".id".to_string(),
10819        })),
10820        ..lsp::CompletionItem::default()
10821    };
10822    let resolved_item_1 = lsp::CompletionItem {
10823        additional_text_edits: Some(vec![lsp::TextEdit {
10824            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
10825            new_text: "!!".to_string(),
10826        }]),
10827        ..unresolved_item_1.clone()
10828    };
10829    let unresolved_item_2 = lsp::CompletionItem {
10830        label: "other".to_string(),
10831        filter_text: Some("other".to_string()),
10832        detail: None,
10833        documentation: None,
10834        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10835            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10836            new_text: ".other".to_string(),
10837        })),
10838        ..lsp::CompletionItem::default()
10839    };
10840    let resolved_item_2 = lsp::CompletionItem {
10841        additional_text_edits: Some(vec![lsp::TextEdit {
10842            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
10843            new_text: "??".to_string(),
10844        }]),
10845        ..unresolved_item_2.clone()
10846    };
10847
10848    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
10849    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
10850    cx.lsp
10851        .server
10852        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10853            let unresolved_item_1 = unresolved_item_1.clone();
10854            let resolved_item_1 = resolved_item_1.clone();
10855            let unresolved_item_2 = unresolved_item_2.clone();
10856            let resolved_item_2 = resolved_item_2.clone();
10857            let resolve_requests_1 = resolve_requests_1.clone();
10858            let resolve_requests_2 = resolve_requests_2.clone();
10859            move |unresolved_request, _| {
10860                let unresolved_item_1 = unresolved_item_1.clone();
10861                let resolved_item_1 = resolved_item_1.clone();
10862                let unresolved_item_2 = unresolved_item_2.clone();
10863                let resolved_item_2 = resolved_item_2.clone();
10864                let resolve_requests_1 = resolve_requests_1.clone();
10865                let resolve_requests_2 = resolve_requests_2.clone();
10866                async move {
10867                    if unresolved_request == unresolved_item_1 {
10868                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
10869                        Ok(resolved_item_1.clone())
10870                    } else if unresolved_request == unresolved_item_2 {
10871                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
10872                        Ok(resolved_item_2.clone())
10873                    } else {
10874                        panic!("Unexpected completion item {unresolved_request:?}")
10875                    }
10876                }
10877            }
10878        })
10879        .detach();
10880
10881    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10882        let unresolved_item_1 = unresolved_item_1.clone();
10883        let unresolved_item_2 = unresolved_item_2.clone();
10884        async move {
10885            Ok(Some(lsp::CompletionResponse::Array(vec![
10886                unresolved_item_1,
10887                unresolved_item_2,
10888            ])))
10889        }
10890    })
10891    .next()
10892    .await;
10893
10894    cx.condition(|editor, _| editor.context_menu_visible())
10895        .await;
10896    cx.update_editor(|editor, _| {
10897        let context_menu = editor.context_menu.borrow_mut();
10898        let context_menu = context_menu
10899            .as_ref()
10900            .expect("Should have the context menu deployed");
10901        match context_menu {
10902            CodeContextMenu::Completions(completions_menu) => {
10903                let completions = completions_menu.completions.borrow_mut();
10904                assert_eq!(
10905                    completions
10906                        .iter()
10907                        .map(|completion| &completion.label.text)
10908                        .collect::<Vec<_>>(),
10909                    vec!["id", "other"]
10910                )
10911            }
10912            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10913        }
10914    });
10915    cx.run_until_parked();
10916
10917    cx.update_editor(|editor, cx| {
10918        editor.context_menu_next(&ContextMenuNext, cx);
10919    });
10920    cx.run_until_parked();
10921    cx.update_editor(|editor, cx| {
10922        editor.context_menu_prev(&ContextMenuPrev, cx);
10923    });
10924    cx.run_until_parked();
10925    cx.update_editor(|editor, cx| {
10926        editor.context_menu_next(&ContextMenuNext, cx);
10927    });
10928    cx.run_until_parked();
10929    cx.update_editor(|editor, cx| {
10930        editor
10931            .compose_completion(&ComposeCompletion::default(), cx)
10932            .expect("No task returned")
10933    })
10934    .await
10935    .expect("Completion failed");
10936    cx.run_until_parked();
10937
10938    cx.update_editor(|editor, cx| {
10939        assert_eq!(
10940            resolve_requests_1.load(atomic::Ordering::Acquire),
10941            1,
10942            "Should always resolve once despite multiple selections"
10943        );
10944        assert_eq!(
10945            resolve_requests_2.load(atomic::Ordering::Acquire),
10946            1,
10947            "Should always resolve once after multiple selections and applying the completion"
10948        );
10949        assert_eq!(
10950            editor.text(cx),
10951            "fn main() { let a = ??.other; }",
10952            "Should use resolved data when applying the completion"
10953        );
10954    });
10955}
10956
10957#[gpui::test]
10958async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10959    init_test(cx, |_| {});
10960
10961    let item_0 = lsp::CompletionItem {
10962        label: "abs".into(),
10963        insert_text: Some("abs".into()),
10964        data: Some(json!({ "very": "special"})),
10965        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10966        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10967            lsp::InsertReplaceEdit {
10968                new_text: "abs".to_string(),
10969                insert: lsp::Range::default(),
10970                replace: lsp::Range::default(),
10971            },
10972        )),
10973        ..lsp::CompletionItem::default()
10974    };
10975    let items = iter::once(item_0.clone())
10976        .chain((11..51).map(|i| lsp::CompletionItem {
10977            label: format!("item_{}", i),
10978            insert_text: Some(format!("item_{}", i)),
10979            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10980            ..lsp::CompletionItem::default()
10981        }))
10982        .collect::<Vec<_>>();
10983
10984    let default_commit_characters = vec!["?".to_string()];
10985    let default_data = json!({ "default": "data"});
10986    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10987    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10988    let default_edit_range = lsp::Range {
10989        start: lsp::Position {
10990            line: 0,
10991            character: 5,
10992        },
10993        end: lsp::Position {
10994            line: 0,
10995            character: 5,
10996        },
10997    };
10998
10999    let item_0_out = lsp::CompletionItem {
11000        commit_characters: Some(default_commit_characters.clone()),
11001        insert_text_format: Some(default_insert_text_format),
11002        ..item_0
11003    };
11004    let items_out = iter::once(item_0_out)
11005        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11006            commit_characters: Some(default_commit_characters.clone()),
11007            data: Some(default_data.clone()),
11008            insert_text_mode: Some(default_insert_text_mode),
11009            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11010                range: default_edit_range,
11011                new_text: item.label.clone(),
11012            })),
11013            ..item.clone()
11014        }))
11015        .collect::<Vec<lsp::CompletionItem>>();
11016
11017    let mut cx = EditorLspTestContext::new_rust(
11018        lsp::ServerCapabilities {
11019            completion_provider: Some(lsp::CompletionOptions {
11020                trigger_characters: Some(vec![".".to_string()]),
11021                resolve_provider: Some(true),
11022                ..Default::default()
11023            }),
11024            ..Default::default()
11025        },
11026        cx,
11027    )
11028    .await;
11029
11030    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11031    cx.simulate_keystroke(".");
11032
11033    let completion_data = default_data.clone();
11034    let completion_characters = default_commit_characters.clone();
11035    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11036        let default_data = completion_data.clone();
11037        let default_commit_characters = completion_characters.clone();
11038        let items = items.clone();
11039        async move {
11040            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11041                items,
11042                item_defaults: Some(lsp::CompletionListItemDefaults {
11043                    data: Some(default_data.clone()),
11044                    commit_characters: Some(default_commit_characters.clone()),
11045                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11046                        default_edit_range,
11047                    )),
11048                    insert_text_format: Some(default_insert_text_format),
11049                    insert_text_mode: Some(default_insert_text_mode),
11050                }),
11051                ..lsp::CompletionList::default()
11052            })))
11053        }
11054    })
11055    .next()
11056    .await;
11057
11058    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11059    cx.lsp
11060        .server
11061        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11062            let closure_resolved_items = resolved_items.clone();
11063            move |item_to_resolve, _| {
11064                let closure_resolved_items = closure_resolved_items.clone();
11065                async move {
11066                    closure_resolved_items.lock().push(item_to_resolve.clone());
11067                    Ok(item_to_resolve)
11068                }
11069            }
11070        })
11071        .detach();
11072
11073    cx.condition(|editor, _| editor.context_menu_visible())
11074        .await;
11075    cx.run_until_parked();
11076    cx.update_editor(|editor, _| {
11077        let menu = editor.context_menu.borrow_mut();
11078        match menu.as_ref().expect("should have the completions menu") {
11079            CodeContextMenu::Completions(completions_menu) => {
11080                assert_eq!(
11081                    completions_menu
11082                        .entries
11083                        .iter()
11084                        .flat_map(|c| match c {
11085                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11086                            _ => None,
11087                        })
11088                        .collect::<Vec<String>>(),
11089                    items_out
11090                        .iter()
11091                        .map(|completion| completion.label.clone())
11092                        .collect::<Vec<String>>()
11093                );
11094            }
11095            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11096        }
11097    });
11098    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11099    // with 4 from the end.
11100    assert_eq!(
11101        *resolved_items.lock(),
11102        [
11103            &items_out[0..16],
11104            &items_out[items_out.len() - 4..items_out.len()]
11105        ]
11106        .concat()
11107        .iter()
11108        .cloned()
11109        .collect::<Vec<lsp::CompletionItem>>()
11110    );
11111    resolved_items.lock().clear();
11112
11113    cx.update_editor(|editor, cx| {
11114        editor.context_menu_prev(&ContextMenuPrev, cx);
11115    });
11116    cx.run_until_parked();
11117    // Completions that have already been resolved are skipped.
11118    assert_eq!(
11119        *resolved_items.lock(),
11120        items_out[items_out.len() - 16..items_out.len() - 4]
11121            .iter()
11122            .cloned()
11123            .collect::<Vec<lsp::CompletionItem>>()
11124    );
11125    resolved_items.lock().clear();
11126}
11127
11128#[gpui::test]
11129async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11130    init_test(cx, |_| {});
11131
11132    let mut cx = EditorLspTestContext::new(
11133        Language::new(
11134            LanguageConfig {
11135                matcher: LanguageMatcher {
11136                    path_suffixes: vec!["jsx".into()],
11137                    ..Default::default()
11138                },
11139                overrides: [(
11140                    "element".into(),
11141                    LanguageConfigOverride {
11142                        word_characters: Override::Set(['-'].into_iter().collect()),
11143                        ..Default::default()
11144                    },
11145                )]
11146                .into_iter()
11147                .collect(),
11148                ..Default::default()
11149            },
11150            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11151        )
11152        .with_override_query("(jsx_self_closing_element) @element")
11153        .unwrap(),
11154        lsp::ServerCapabilities {
11155            completion_provider: Some(lsp::CompletionOptions {
11156                trigger_characters: Some(vec![":".to_string()]),
11157                ..Default::default()
11158            }),
11159            ..Default::default()
11160        },
11161        cx,
11162    )
11163    .await;
11164
11165    cx.lsp
11166        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11167            Ok(Some(lsp::CompletionResponse::Array(vec![
11168                lsp::CompletionItem {
11169                    label: "bg-blue".into(),
11170                    ..Default::default()
11171                },
11172                lsp::CompletionItem {
11173                    label: "bg-red".into(),
11174                    ..Default::default()
11175                },
11176                lsp::CompletionItem {
11177                    label: "bg-yellow".into(),
11178                    ..Default::default()
11179                },
11180            ])))
11181        });
11182
11183    cx.set_state(r#"<p class="bgˇ" />"#);
11184
11185    // Trigger completion when typing a dash, because the dash is an extra
11186    // word character in the 'element' scope, which contains the cursor.
11187    cx.simulate_keystroke("-");
11188    cx.executor().run_until_parked();
11189    cx.update_editor(|editor, _| {
11190        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11191        {
11192            assert_eq!(
11193                completion_menu_entries(&menu.entries),
11194                &["bg-red", "bg-blue", "bg-yellow"]
11195            );
11196        } else {
11197            panic!("expected completion menu to be open");
11198        }
11199    });
11200
11201    cx.simulate_keystroke("l");
11202    cx.executor().run_until_parked();
11203    cx.update_editor(|editor, _| {
11204        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11205        {
11206            assert_eq!(
11207                completion_menu_entries(&menu.entries),
11208                &["bg-blue", "bg-yellow"]
11209            );
11210        } else {
11211            panic!("expected completion menu to be open");
11212        }
11213    });
11214
11215    // When filtering completions, consider the character after the '-' to
11216    // be the start of a subword.
11217    cx.set_state(r#"<p class="yelˇ" />"#);
11218    cx.simulate_keystroke("l");
11219    cx.executor().run_until_parked();
11220    cx.update_editor(|editor, _| {
11221        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11222        {
11223            assert_eq!(completion_menu_entries(&menu.entries), &["bg-yellow"]);
11224        } else {
11225            panic!("expected completion menu to be open");
11226        }
11227    });
11228}
11229
11230fn completion_menu_entries(entries: &[CompletionEntry]) -> Vec<&str> {
11231    entries
11232        .iter()
11233        .flat_map(|e| match e {
11234            CompletionEntry::Match(mat) => Some(mat.string.as_str()),
11235            _ => None,
11236        })
11237        .collect()
11238}
11239
11240#[gpui::test]
11241async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11242    init_test(cx, |settings| {
11243        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11244            FormatterList(vec![Formatter::Prettier].into()),
11245        ))
11246    });
11247
11248    let fs = FakeFs::new(cx.executor());
11249    fs.insert_file("/file.ts", Default::default()).await;
11250
11251    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11252    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11253
11254    language_registry.add(Arc::new(Language::new(
11255        LanguageConfig {
11256            name: "TypeScript".into(),
11257            matcher: LanguageMatcher {
11258                path_suffixes: vec!["ts".to_string()],
11259                ..Default::default()
11260            },
11261            ..Default::default()
11262        },
11263        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11264    )));
11265    update_test_language_settings(cx, |settings| {
11266        settings.defaults.prettier = Some(PrettierSettings {
11267            allowed: true,
11268            ..PrettierSettings::default()
11269        });
11270    });
11271
11272    let test_plugin = "test_plugin";
11273    let _ = language_registry.register_fake_lsp(
11274        "TypeScript",
11275        FakeLspAdapter {
11276            prettier_plugins: vec![test_plugin],
11277            ..Default::default()
11278        },
11279    );
11280
11281    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11282    let buffer = project
11283        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11284        .await
11285        .unwrap();
11286
11287    let buffer_text = "one\ntwo\nthree\n";
11288    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11289    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11290    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11291
11292    editor
11293        .update(cx, |editor, cx| {
11294            editor.perform_format(
11295                project.clone(),
11296                FormatTrigger::Manual,
11297                FormatTarget::Buffer,
11298                cx,
11299            )
11300        })
11301        .unwrap()
11302        .await;
11303    assert_eq!(
11304        editor.update(cx, |editor, cx| editor.text(cx)),
11305        buffer_text.to_string() + prettier_format_suffix,
11306        "Test prettier formatting was not applied to the original buffer text",
11307    );
11308
11309    update_test_language_settings(cx, |settings| {
11310        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11311    });
11312    let format = editor.update(cx, |editor, cx| {
11313        editor.perform_format(
11314            project.clone(),
11315            FormatTrigger::Manual,
11316            FormatTarget::Buffer,
11317            cx,
11318        )
11319    });
11320    format.await.unwrap();
11321    assert_eq!(
11322        editor.update(cx, |editor, cx| editor.text(cx)),
11323        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11324        "Autoformatting (via test prettier) was not applied to the original buffer text",
11325    );
11326}
11327
11328#[gpui::test]
11329async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11330    init_test(cx, |_| {});
11331    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11332    let base_text = indoc! {r#"
11333        struct Row;
11334        struct Row1;
11335        struct Row2;
11336
11337        struct Row4;
11338        struct Row5;
11339        struct Row6;
11340
11341        struct Row8;
11342        struct Row9;
11343        struct Row10;"#};
11344
11345    // When addition hunks are not adjacent to carets, no hunk revert is performed
11346    assert_hunk_revert(
11347        indoc! {r#"struct Row;
11348                   struct Row1;
11349                   struct Row1.1;
11350                   struct Row1.2;
11351                   struct Row2;ˇ
11352
11353                   struct Row4;
11354                   struct Row5;
11355                   struct Row6;
11356
11357                   struct Row8;
11358                   ˇstruct Row9;
11359                   struct Row9.1;
11360                   struct Row9.2;
11361                   struct Row9.3;
11362                   struct Row10;"#},
11363        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11364        indoc! {r#"struct Row;
11365                   struct Row1;
11366                   struct Row1.1;
11367                   struct Row1.2;
11368                   struct Row2;ˇ
11369
11370                   struct Row4;
11371                   struct Row5;
11372                   struct Row6;
11373
11374                   struct Row8;
11375                   ˇstruct Row9;
11376                   struct Row9.1;
11377                   struct Row9.2;
11378                   struct Row9.3;
11379                   struct Row10;"#},
11380        base_text,
11381        &mut cx,
11382    );
11383    // Same for selections
11384    assert_hunk_revert(
11385        indoc! {r#"struct Row;
11386                   struct Row1;
11387                   struct Row2;
11388                   struct Row2.1;
11389                   struct Row2.2;
11390                   «ˇ
11391                   struct Row4;
11392                   struct» Row5;
11393                   «struct Row6;
11394                   ˇ»
11395                   struct Row9.1;
11396                   struct Row9.2;
11397                   struct Row9.3;
11398                   struct Row8;
11399                   struct Row9;
11400                   struct Row10;"#},
11401        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11402        indoc! {r#"struct Row;
11403                   struct Row1;
11404                   struct Row2;
11405                   struct Row2.1;
11406                   struct Row2.2;
11407                   «ˇ
11408                   struct Row4;
11409                   struct» Row5;
11410                   «struct Row6;
11411                   ˇ»
11412                   struct Row9.1;
11413                   struct Row9.2;
11414                   struct Row9.3;
11415                   struct Row8;
11416                   struct Row9;
11417                   struct Row10;"#},
11418        base_text,
11419        &mut cx,
11420    );
11421
11422    // When carets and selections intersect the addition hunks, those are reverted.
11423    // Adjacent carets got merged.
11424    assert_hunk_revert(
11425        indoc! {r#"struct Row;
11426                   ˇ// something on the top
11427                   struct Row1;
11428                   struct Row2;
11429                   struct Roˇw3.1;
11430                   struct Row2.2;
11431                   struct Row2.3;ˇ
11432
11433                   struct Row4;
11434                   struct ˇRow5.1;
11435                   struct Row5.2;
11436                   struct «Rowˇ»5.3;
11437                   struct Row5;
11438                   struct Row6;
11439                   ˇ
11440                   struct Row9.1;
11441                   struct «Rowˇ»9.2;
11442                   struct «ˇRow»9.3;
11443                   struct Row8;
11444                   struct Row9;
11445                   «ˇ// something on bottom»
11446                   struct Row10;"#},
11447        vec![
11448            DiffHunkStatus::Added,
11449            DiffHunkStatus::Added,
11450            DiffHunkStatus::Added,
11451            DiffHunkStatus::Added,
11452            DiffHunkStatus::Added,
11453        ],
11454        indoc! {r#"struct Row;
11455                   ˇstruct Row1;
11456                   struct Row2;
11457                   ˇ
11458                   struct Row4;
11459                   ˇstruct Row5;
11460                   struct Row6;
11461                   ˇ
11462                   ˇstruct Row8;
11463                   struct Row9;
11464                   ˇstruct Row10;"#},
11465        base_text,
11466        &mut cx,
11467    );
11468}
11469
11470#[gpui::test]
11471async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11472    init_test(cx, |_| {});
11473    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11474    let base_text = indoc! {r#"
11475        struct Row;
11476        struct Row1;
11477        struct Row2;
11478
11479        struct Row4;
11480        struct Row5;
11481        struct Row6;
11482
11483        struct Row8;
11484        struct Row9;
11485        struct Row10;"#};
11486
11487    // Modification hunks behave the same as the addition ones.
11488    assert_hunk_revert(
11489        indoc! {r#"struct Row;
11490                   struct Row1;
11491                   struct Row33;
11492                   ˇ
11493                   struct Row4;
11494                   struct Row5;
11495                   struct Row6;
11496                   ˇ
11497                   struct Row99;
11498                   struct Row9;
11499                   struct Row10;"#},
11500        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11501        indoc! {r#"struct Row;
11502                   struct Row1;
11503                   struct Row33;
11504                   ˇ
11505                   struct Row4;
11506                   struct Row5;
11507                   struct Row6;
11508                   ˇ
11509                   struct Row99;
11510                   struct Row9;
11511                   struct Row10;"#},
11512        base_text,
11513        &mut cx,
11514    );
11515    assert_hunk_revert(
11516        indoc! {r#"struct Row;
11517                   struct Row1;
11518                   struct Row33;
11519                   «ˇ
11520                   struct Row4;
11521                   struct» Row5;
11522                   «struct Row6;
11523                   ˇ»
11524                   struct Row99;
11525                   struct Row9;
11526                   struct Row10;"#},
11527        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11528        indoc! {r#"struct Row;
11529                   struct Row1;
11530                   struct Row33;
11531                   «ˇ
11532                   struct Row4;
11533                   struct» Row5;
11534                   «struct Row6;
11535                   ˇ»
11536                   struct Row99;
11537                   struct Row9;
11538                   struct Row10;"#},
11539        base_text,
11540        &mut cx,
11541    );
11542
11543    assert_hunk_revert(
11544        indoc! {r#"ˇstruct Row1.1;
11545                   struct Row1;
11546                   «ˇstr»uct Row22;
11547
11548                   struct ˇRow44;
11549                   struct Row5;
11550                   struct «Rˇ»ow66;ˇ
11551
11552                   «struˇ»ct Row88;
11553                   struct Row9;
11554                   struct Row1011;ˇ"#},
11555        vec![
11556            DiffHunkStatus::Modified,
11557            DiffHunkStatus::Modified,
11558            DiffHunkStatus::Modified,
11559            DiffHunkStatus::Modified,
11560            DiffHunkStatus::Modified,
11561            DiffHunkStatus::Modified,
11562        ],
11563        indoc! {r#"struct Row;
11564                   ˇstruct Row1;
11565                   struct Row2;
11566                   ˇ
11567                   struct Row4;
11568                   ˇstruct Row5;
11569                   struct Row6;
11570                   ˇ
11571                   struct Row8;
11572                   ˇstruct Row9;
11573                   struct Row10;ˇ"#},
11574        base_text,
11575        &mut cx,
11576    );
11577}
11578
11579#[gpui::test]
11580async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11581    init_test(cx, |_| {});
11582    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11583    let base_text = indoc! {r#"struct Row;
11584struct Row1;
11585struct Row2;
11586
11587struct Row4;
11588struct Row5;
11589struct Row6;
11590
11591struct Row8;
11592struct Row9;
11593struct Row10;"#};
11594
11595    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11596    assert_hunk_revert(
11597        indoc! {r#"struct Row;
11598                   struct Row2;
11599
11600                   ˇstruct Row4;
11601                   struct Row5;
11602                   struct Row6;
11603                   ˇ
11604                   struct Row8;
11605                   struct Row10;"#},
11606        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11607        indoc! {r#"struct Row;
11608                   struct Row2;
11609
11610                   ˇstruct Row4;
11611                   struct Row5;
11612                   struct Row6;
11613                   ˇ
11614                   struct Row8;
11615                   struct Row10;"#},
11616        base_text,
11617        &mut cx,
11618    );
11619    assert_hunk_revert(
11620        indoc! {r#"struct Row;
11621                   struct Row2;
11622
11623                   «ˇstruct Row4;
11624                   struct» Row5;
11625                   «struct Row6;
11626                   ˇ»
11627                   struct Row8;
11628                   struct Row10;"#},
11629        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11630        indoc! {r#"struct Row;
11631                   struct Row2;
11632
11633                   «ˇstruct Row4;
11634                   struct» Row5;
11635                   «struct Row6;
11636                   ˇ»
11637                   struct Row8;
11638                   struct Row10;"#},
11639        base_text,
11640        &mut cx,
11641    );
11642
11643    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11644    assert_hunk_revert(
11645        indoc! {r#"struct Row;
11646                   ˇstruct Row2;
11647
11648                   struct Row4;
11649                   struct Row5;
11650                   struct Row6;
11651
11652                   struct Row8;ˇ
11653                   struct Row10;"#},
11654        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11655        indoc! {r#"struct Row;
11656                   struct Row1;
11657                   ˇstruct Row2;
11658
11659                   struct Row4;
11660                   struct Row5;
11661                   struct Row6;
11662
11663                   struct Row8;ˇ
11664                   struct Row9;
11665                   struct Row10;"#},
11666        base_text,
11667        &mut cx,
11668    );
11669    assert_hunk_revert(
11670        indoc! {r#"struct Row;
11671                   struct Row2«ˇ;
11672                   struct Row4;
11673                   struct» Row5;
11674                   «struct Row6;
11675
11676                   struct Row8;ˇ»
11677                   struct Row10;"#},
11678        vec![
11679            DiffHunkStatus::Removed,
11680            DiffHunkStatus::Removed,
11681            DiffHunkStatus::Removed,
11682        ],
11683        indoc! {r#"struct Row;
11684                   struct Row1;
11685                   struct Row2«ˇ;
11686
11687                   struct Row4;
11688                   struct» Row5;
11689                   «struct Row6;
11690
11691                   struct Row8;ˇ»
11692                   struct Row9;
11693                   struct Row10;"#},
11694        base_text,
11695        &mut cx,
11696    );
11697}
11698
11699#[gpui::test]
11700async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11701    init_test(cx, |_| {});
11702
11703    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11704    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11705    let base_text_3 =
11706        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11707
11708    let text_1 = edit_first_char_of_every_line(base_text_1);
11709    let text_2 = edit_first_char_of_every_line(base_text_2);
11710    let text_3 = edit_first_char_of_every_line(base_text_3);
11711
11712    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11713    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11714    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11715
11716    let multibuffer = cx.new_model(|cx| {
11717        let mut multibuffer = MultiBuffer::new(ReadWrite);
11718        multibuffer.push_excerpts(
11719            buffer_1.clone(),
11720            [
11721                ExcerptRange {
11722                    context: Point::new(0, 0)..Point::new(3, 0),
11723                    primary: None,
11724                },
11725                ExcerptRange {
11726                    context: Point::new(5, 0)..Point::new(7, 0),
11727                    primary: None,
11728                },
11729                ExcerptRange {
11730                    context: Point::new(9, 0)..Point::new(10, 4),
11731                    primary: None,
11732                },
11733            ],
11734            cx,
11735        );
11736        multibuffer.push_excerpts(
11737            buffer_2.clone(),
11738            [
11739                ExcerptRange {
11740                    context: Point::new(0, 0)..Point::new(3, 0),
11741                    primary: None,
11742                },
11743                ExcerptRange {
11744                    context: Point::new(5, 0)..Point::new(7, 0),
11745                    primary: None,
11746                },
11747                ExcerptRange {
11748                    context: Point::new(9, 0)..Point::new(10, 4),
11749                    primary: None,
11750                },
11751            ],
11752            cx,
11753        );
11754        multibuffer.push_excerpts(
11755            buffer_3.clone(),
11756            [
11757                ExcerptRange {
11758                    context: Point::new(0, 0)..Point::new(3, 0),
11759                    primary: None,
11760                },
11761                ExcerptRange {
11762                    context: Point::new(5, 0)..Point::new(7, 0),
11763                    primary: None,
11764                },
11765                ExcerptRange {
11766                    context: Point::new(9, 0)..Point::new(10, 4),
11767                    primary: None,
11768                },
11769            ],
11770            cx,
11771        );
11772        multibuffer
11773    });
11774
11775    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11776    editor.update(cx, |editor, cx| {
11777        for (buffer, diff_base) in [
11778            (buffer_1.clone(), base_text_1),
11779            (buffer_2.clone(), base_text_2),
11780            (buffer_3.clone(), base_text_3),
11781        ] {
11782            let change_set = cx.new_model(|cx| {
11783                BufferChangeSet::new_with_base_text(
11784                    diff_base.to_string(),
11785                    buffer.read(cx).text_snapshot(),
11786                    cx,
11787                )
11788            });
11789            editor.diff_map.add_change_set(change_set, cx)
11790        }
11791    });
11792    cx.executor().run_until_parked();
11793
11794    editor.update(cx, |editor, cx| {
11795        assert_eq!(editor.text(cx), "Xaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
11796        editor.select_all(&SelectAll, cx);
11797        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11798    });
11799    cx.executor().run_until_parked();
11800
11801    // When all ranges are selected, all buffer hunks are reverted.
11802    editor.update(cx, |editor, cx| {
11803        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");
11804    });
11805    buffer_1.update(cx, |buffer, _| {
11806        assert_eq!(buffer.text(), base_text_1);
11807    });
11808    buffer_2.update(cx, |buffer, _| {
11809        assert_eq!(buffer.text(), base_text_2);
11810    });
11811    buffer_3.update(cx, |buffer, _| {
11812        assert_eq!(buffer.text(), base_text_3);
11813    });
11814
11815    editor.update(cx, |editor, cx| {
11816        editor.undo(&Default::default(), cx);
11817    });
11818
11819    editor.update(cx, |editor, cx| {
11820        editor.change_selections(None, cx, |s| {
11821            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11822        });
11823        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11824    });
11825
11826    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11827    // but not affect buffer_2 and its related excerpts.
11828    editor.update(cx, |editor, cx| {
11829        assert_eq!(
11830            editor.text(cx),
11831            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
11832        );
11833    });
11834    buffer_1.update(cx, |buffer, _| {
11835        assert_eq!(buffer.text(), base_text_1);
11836    });
11837    buffer_2.update(cx, |buffer, _| {
11838        assert_eq!(
11839            buffer.text(),
11840            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11841        );
11842    });
11843    buffer_3.update(cx, |buffer, _| {
11844        assert_eq!(
11845            buffer.text(),
11846            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11847        );
11848    });
11849
11850    fn edit_first_char_of_every_line(text: &str) -> String {
11851        text.split('\n')
11852            .map(|line| format!("X{}", &line[1..]))
11853            .collect::<Vec<_>>()
11854            .join("\n")
11855    }
11856}
11857
11858#[gpui::test]
11859async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11860    init_test(cx, |_| {});
11861
11862    let cols = 4;
11863    let rows = 10;
11864    let sample_text_1 = sample_text(rows, cols, 'a');
11865    assert_eq!(
11866        sample_text_1,
11867        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11868    );
11869    let sample_text_2 = sample_text(rows, cols, 'l');
11870    assert_eq!(
11871        sample_text_2,
11872        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11873    );
11874    let sample_text_3 = sample_text(rows, cols, 'v');
11875    assert_eq!(
11876        sample_text_3,
11877        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11878    );
11879
11880    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11881    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11882    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11883
11884    let multi_buffer = cx.new_model(|cx| {
11885        let mut multibuffer = MultiBuffer::new(ReadWrite);
11886        multibuffer.push_excerpts(
11887            buffer_1.clone(),
11888            [
11889                ExcerptRange {
11890                    context: Point::new(0, 0)..Point::new(3, 0),
11891                    primary: None,
11892                },
11893                ExcerptRange {
11894                    context: Point::new(5, 0)..Point::new(7, 0),
11895                    primary: None,
11896                },
11897                ExcerptRange {
11898                    context: Point::new(9, 0)..Point::new(10, 4),
11899                    primary: None,
11900                },
11901            ],
11902            cx,
11903        );
11904        multibuffer.push_excerpts(
11905            buffer_2.clone(),
11906            [
11907                ExcerptRange {
11908                    context: Point::new(0, 0)..Point::new(3, 0),
11909                    primary: None,
11910                },
11911                ExcerptRange {
11912                    context: Point::new(5, 0)..Point::new(7, 0),
11913                    primary: None,
11914                },
11915                ExcerptRange {
11916                    context: Point::new(9, 0)..Point::new(10, 4),
11917                    primary: None,
11918                },
11919            ],
11920            cx,
11921        );
11922        multibuffer.push_excerpts(
11923            buffer_3.clone(),
11924            [
11925                ExcerptRange {
11926                    context: Point::new(0, 0)..Point::new(3, 0),
11927                    primary: None,
11928                },
11929                ExcerptRange {
11930                    context: Point::new(5, 0)..Point::new(7, 0),
11931                    primary: None,
11932                },
11933                ExcerptRange {
11934                    context: Point::new(9, 0)..Point::new(10, 4),
11935                    primary: None,
11936                },
11937            ],
11938            cx,
11939        );
11940        multibuffer
11941    });
11942
11943    let fs = FakeFs::new(cx.executor());
11944    fs.insert_tree(
11945        "/a",
11946        json!({
11947            "main.rs": sample_text_1,
11948            "other.rs": sample_text_2,
11949            "lib.rs": sample_text_3,
11950        }),
11951    )
11952    .await;
11953    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11954    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11955    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11956    let multi_buffer_editor = cx.new_view(|cx| {
11957        Editor::new(
11958            EditorMode::Full,
11959            multi_buffer,
11960            Some(project.clone()),
11961            true,
11962            cx,
11963        )
11964    });
11965    let multibuffer_item_id = workspace
11966        .update(cx, |workspace, cx| {
11967            assert!(
11968                workspace.active_item(cx).is_none(),
11969                "active item should be None before the first item is added"
11970            );
11971            workspace.add_item_to_active_pane(
11972                Box::new(multi_buffer_editor.clone()),
11973                None,
11974                true,
11975                cx,
11976            );
11977            let active_item = workspace
11978                .active_item(cx)
11979                .expect("should have an active item after adding the multi buffer");
11980            assert!(
11981                !active_item.is_singleton(cx),
11982                "A multi buffer was expected to active after adding"
11983            );
11984            active_item.item_id()
11985        })
11986        .unwrap();
11987    cx.executor().run_until_parked();
11988
11989    multi_buffer_editor.update(cx, |editor, cx| {
11990        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11991        editor.open_excerpts(&OpenExcerpts, cx);
11992    });
11993    cx.executor().run_until_parked();
11994    let first_item_id = workspace
11995        .update(cx, |workspace, cx| {
11996            let active_item = workspace
11997                .active_item(cx)
11998                .expect("should have an active item after navigating into the 1st buffer");
11999            let first_item_id = active_item.item_id();
12000            assert_ne!(
12001                first_item_id, multibuffer_item_id,
12002                "Should navigate into the 1st buffer and activate it"
12003            );
12004            assert!(
12005                active_item.is_singleton(cx),
12006                "New active item should be a singleton buffer"
12007            );
12008            assert_eq!(
12009                active_item
12010                    .act_as::<Editor>(cx)
12011                    .expect("should have navigated into an editor for the 1st buffer")
12012                    .read(cx)
12013                    .text(cx),
12014                sample_text_1
12015            );
12016
12017            workspace
12018                .go_back(workspace.active_pane().downgrade(), cx)
12019                .detach_and_log_err(cx);
12020
12021            first_item_id
12022        })
12023        .unwrap();
12024    cx.executor().run_until_parked();
12025    workspace
12026        .update(cx, |workspace, cx| {
12027            let active_item = workspace
12028                .active_item(cx)
12029                .expect("should have an active item after navigating back");
12030            assert_eq!(
12031                active_item.item_id(),
12032                multibuffer_item_id,
12033                "Should navigate back to the multi buffer"
12034            );
12035            assert!(!active_item.is_singleton(cx));
12036        })
12037        .unwrap();
12038
12039    multi_buffer_editor.update(cx, |editor, cx| {
12040        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12041            s.select_ranges(Some(39..40))
12042        });
12043        editor.open_excerpts(&OpenExcerpts, cx);
12044    });
12045    cx.executor().run_until_parked();
12046    let second_item_id = workspace
12047        .update(cx, |workspace, cx| {
12048            let active_item = workspace
12049                .active_item(cx)
12050                .expect("should have an active item after navigating into the 2nd buffer");
12051            let second_item_id = active_item.item_id();
12052            assert_ne!(
12053                second_item_id, multibuffer_item_id,
12054                "Should navigate away from the multibuffer"
12055            );
12056            assert_ne!(
12057                second_item_id, first_item_id,
12058                "Should navigate into the 2nd buffer and activate it"
12059            );
12060            assert!(
12061                active_item.is_singleton(cx),
12062                "New active item should be a singleton buffer"
12063            );
12064            assert_eq!(
12065                active_item
12066                    .act_as::<Editor>(cx)
12067                    .expect("should have navigated into an editor")
12068                    .read(cx)
12069                    .text(cx),
12070                sample_text_2
12071            );
12072
12073            workspace
12074                .go_back(workspace.active_pane().downgrade(), cx)
12075                .detach_and_log_err(cx);
12076
12077            second_item_id
12078        })
12079        .unwrap();
12080    cx.executor().run_until_parked();
12081    workspace
12082        .update(cx, |workspace, cx| {
12083            let active_item = workspace
12084                .active_item(cx)
12085                .expect("should have an active item after navigating back from the 2nd buffer");
12086            assert_eq!(
12087                active_item.item_id(),
12088                multibuffer_item_id,
12089                "Should navigate back from the 2nd buffer to the multi buffer"
12090            );
12091            assert!(!active_item.is_singleton(cx));
12092        })
12093        .unwrap();
12094
12095    multi_buffer_editor.update(cx, |editor, cx| {
12096        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12097            s.select_ranges(Some(70..70))
12098        });
12099        editor.open_excerpts(&OpenExcerpts, cx);
12100    });
12101    cx.executor().run_until_parked();
12102    workspace
12103        .update(cx, |workspace, cx| {
12104            let active_item = workspace
12105                .active_item(cx)
12106                .expect("should have an active item after navigating into the 3rd buffer");
12107            let third_item_id = active_item.item_id();
12108            assert_ne!(
12109                third_item_id, multibuffer_item_id,
12110                "Should navigate into the 3rd buffer and activate it"
12111            );
12112            assert_ne!(third_item_id, first_item_id);
12113            assert_ne!(third_item_id, second_item_id);
12114            assert!(
12115                active_item.is_singleton(cx),
12116                "New active item should be a singleton buffer"
12117            );
12118            assert_eq!(
12119                active_item
12120                    .act_as::<Editor>(cx)
12121                    .expect("should have navigated into an editor")
12122                    .read(cx)
12123                    .text(cx),
12124                sample_text_3
12125            );
12126
12127            workspace
12128                .go_back(workspace.active_pane().downgrade(), cx)
12129                .detach_and_log_err(cx);
12130        })
12131        .unwrap();
12132    cx.executor().run_until_parked();
12133    workspace
12134        .update(cx, |workspace, cx| {
12135            let active_item = workspace
12136                .active_item(cx)
12137                .expect("should have an active item after navigating back from the 3rd buffer");
12138            assert_eq!(
12139                active_item.item_id(),
12140                multibuffer_item_id,
12141                "Should navigate back from the 3rd buffer to the multi buffer"
12142            );
12143            assert!(!active_item.is_singleton(cx));
12144        })
12145        .unwrap();
12146}
12147
12148#[gpui::test]
12149async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12150    init_test(cx, |_| {});
12151
12152    let mut cx = EditorTestContext::new(cx).await;
12153
12154    let diff_base = r#"
12155        use some::mod;
12156
12157        const A: u32 = 42;
12158
12159        fn main() {
12160            println!("hello");
12161
12162            println!("world");
12163        }
12164        "#
12165    .unindent();
12166
12167    cx.set_state(
12168        &r#"
12169        use some::modified;
12170
12171        ˇ
12172        fn main() {
12173            println!("hello there");
12174
12175            println!("around the");
12176            println!("world");
12177        }
12178        "#
12179        .unindent(),
12180    );
12181
12182    cx.set_diff_base(&diff_base);
12183    executor.run_until_parked();
12184
12185    cx.update_editor(|editor, cx| {
12186        editor.go_to_next_hunk(&GoToHunk, cx);
12187        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12188    });
12189    executor.run_until_parked();
12190    cx.assert_state_with_diff(
12191        r#"
12192          use some::modified;
12193
12194
12195          fn main() {
12196        -     println!("hello");
12197        + ˇ    println!("hello there");
12198
12199              println!("around the");
12200              println!("world");
12201          }
12202        "#
12203        .unindent(),
12204    );
12205
12206    cx.update_editor(|editor, cx| {
12207        for _ in 0..3 {
12208            editor.go_to_next_hunk(&GoToHunk, cx);
12209            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12210        }
12211    });
12212    executor.run_until_parked();
12213    cx.assert_state_with_diff(
12214        r#"
12215        - use some::mod;
12216        + use some::modified;
12217
12218        - const A: u32 = 42;
12219          ˇ
12220          fn main() {
12221        -     println!("hello");
12222        +     println!("hello there");
12223
12224        +     println!("around the");
12225              println!("world");
12226          }
12227        "#
12228        .unindent(),
12229    );
12230
12231    cx.update_editor(|editor, cx| {
12232        editor.cancel(&Cancel, cx);
12233    });
12234
12235    cx.assert_state_with_diff(
12236        r#"
12237          use some::modified;
12238
12239          ˇ
12240          fn main() {
12241              println!("hello there");
12242
12243              println!("around the");
12244              println!("world");
12245          }
12246        "#
12247        .unindent(),
12248    );
12249}
12250
12251#[gpui::test]
12252async fn test_diff_base_change_with_expanded_diff_hunks(
12253    executor: BackgroundExecutor,
12254    cx: &mut gpui::TestAppContext,
12255) {
12256    init_test(cx, |_| {});
12257
12258    let mut cx = EditorTestContext::new(cx).await;
12259
12260    let diff_base = r#"
12261        use some::mod1;
12262        use some::mod2;
12263
12264        const A: u32 = 42;
12265        const B: u32 = 42;
12266        const C: u32 = 42;
12267
12268        fn main() {
12269            println!("hello");
12270
12271            println!("world");
12272        }
12273        "#
12274    .unindent();
12275
12276    cx.set_state(
12277        &r#"
12278        use some::mod2;
12279
12280        const A: u32 = 42;
12281        const C: u32 = 42;
12282
12283        fn main(ˇ) {
12284            //println!("hello");
12285
12286            println!("world");
12287            //
12288            //
12289        }
12290        "#
12291        .unindent(),
12292    );
12293
12294    cx.set_diff_base(&diff_base);
12295    executor.run_until_parked();
12296
12297    cx.update_editor(|editor, cx| {
12298        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12299    });
12300    executor.run_until_parked();
12301    cx.assert_state_with_diff(
12302        r#"
12303        - use some::mod1;
12304          use some::mod2;
12305
12306          const A: u32 = 42;
12307        - const B: u32 = 42;
12308          const C: u32 = 42;
12309
12310          fn main(ˇ) {
12311        -     println!("hello");
12312        +     //println!("hello");
12313
12314              println!("world");
12315        +     //
12316        +     //
12317          }
12318        "#
12319        .unindent(),
12320    );
12321
12322    cx.set_diff_base("new diff base!");
12323    executor.run_until_parked();
12324    cx.assert_state_with_diff(
12325        r#"
12326          use some::mod2;
12327
12328          const A: u32 = 42;
12329          const C: u32 = 42;
12330
12331          fn main(ˇ) {
12332              //println!("hello");
12333
12334              println!("world");
12335              //
12336              //
12337          }
12338        "#
12339        .unindent(),
12340    );
12341
12342    cx.update_editor(|editor, cx| {
12343        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12344    });
12345    executor.run_until_parked();
12346    cx.assert_state_with_diff(
12347        r#"
12348        - new diff base!
12349        + use some::mod2;
12350        +
12351        + const A: u32 = 42;
12352        + const C: u32 = 42;
12353        +
12354        + fn main(ˇ) {
12355        +     //println!("hello");
12356        +
12357        +     println!("world");
12358        +     //
12359        +     //
12360        + }
12361        "#
12362        .unindent(),
12363    );
12364}
12365
12366#[gpui::test]
12367async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12368    init_test(cx, |_| {});
12369
12370    let mut cx = EditorTestContext::new(cx).await;
12371
12372    let diff_base = r#"
12373        use some::mod1;
12374        use some::mod2;
12375
12376        const A: u32 = 42;
12377        const B: u32 = 42;
12378        const C: u32 = 42;
12379
12380        fn main() {
12381            println!("hello");
12382
12383            println!("world");
12384        }
12385
12386        fn another() {
12387            println!("another");
12388        }
12389
12390        fn another2() {
12391            println!("another2");
12392        }
12393        "#
12394    .unindent();
12395
12396    cx.set_state(
12397        &r#"
12398        «use some::mod2;
12399
12400        const A: u32 = 42;
12401        const C: u32 = 42;
12402
12403        fn main() {
12404            //println!("hello");
12405
12406            println!("world");
12407            //
12408            //ˇ»
12409        }
12410
12411        fn another() {
12412            println!("another");
12413            println!("another");
12414        }
12415
12416            println!("another2");
12417        }
12418        "#
12419        .unindent(),
12420    );
12421
12422    cx.set_diff_base(&diff_base);
12423    executor.run_until_parked();
12424
12425    cx.update_editor(|editor, cx| {
12426        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12427    });
12428    executor.run_until_parked();
12429
12430    cx.assert_state_with_diff(
12431        r#"
12432        - use some::mod1;
12433          «use some::mod2;
12434
12435          const A: u32 = 42;
12436        - const B: u32 = 42;
12437          const C: u32 = 42;
12438
12439          fn main() {
12440        -     println!("hello");
12441        +     //println!("hello");
12442
12443              println!("world");
12444        +     //
12445        +     //ˇ»
12446          }
12447
12448          fn another() {
12449              println!("another");
12450        +     println!("another");
12451          }
12452
12453        - fn another2() {
12454              println!("another2");
12455          }
12456        "#
12457        .unindent(),
12458    );
12459
12460    // Fold across some of the diff hunks. They should no longer appear expanded.
12461    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12462    cx.executor().run_until_parked();
12463
12464    // Hunks are not shown if their position is within a fold
12465    cx.assert_state_with_diff(
12466        r#"
12467          «use some::mod2;
12468
12469          const A: u32 = 42;
12470          const C: u32 = 42;
12471
12472          fn main() {
12473              //println!("hello");
12474
12475              println!("world");
12476              //
12477              //ˇ»
12478          }
12479
12480          fn another() {
12481              println!("another");
12482        +     println!("another");
12483          }
12484
12485        - fn another2() {
12486              println!("another2");
12487          }
12488        "#
12489        .unindent(),
12490    );
12491
12492    cx.update_editor(|editor, cx| {
12493        editor.select_all(&SelectAll, cx);
12494        editor.unfold_lines(&UnfoldLines, cx);
12495    });
12496    cx.executor().run_until_parked();
12497
12498    // The deletions reappear when unfolding.
12499    cx.assert_state_with_diff(
12500        r#"
12501        - use some::mod1;
12502          «use some::mod2;
12503
12504          const A: u32 = 42;
12505        - const B: u32 = 42;
12506          const C: u32 = 42;
12507
12508          fn main() {
12509        -     println!("hello");
12510        +     //println!("hello");
12511
12512              println!("world");
12513        +     //
12514        +     //
12515          }
12516
12517          fn another() {
12518              println!("another");
12519        +     println!("another");
12520          }
12521
12522        - fn another2() {
12523              println!("another2");
12524          }
12525          ˇ»"#
12526        .unindent(),
12527    );
12528}
12529
12530#[gpui::test]
12531async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12532    init_test(cx, |_| {});
12533
12534    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12535    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12536    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12537    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12538    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12539    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12540
12541    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12542    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12543    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12544
12545    let multi_buffer = cx.new_model(|cx| {
12546        let mut multibuffer = MultiBuffer::new(ReadWrite);
12547        multibuffer.push_excerpts(
12548            buffer_1.clone(),
12549            [
12550                ExcerptRange {
12551                    context: Point::new(0, 0)..Point::new(3, 0),
12552                    primary: None,
12553                },
12554                ExcerptRange {
12555                    context: Point::new(5, 0)..Point::new(7, 0),
12556                    primary: None,
12557                },
12558                ExcerptRange {
12559                    context: Point::new(9, 0)..Point::new(10, 3),
12560                    primary: None,
12561                },
12562            ],
12563            cx,
12564        );
12565        multibuffer.push_excerpts(
12566            buffer_2.clone(),
12567            [
12568                ExcerptRange {
12569                    context: Point::new(0, 0)..Point::new(3, 0),
12570                    primary: None,
12571                },
12572                ExcerptRange {
12573                    context: Point::new(5, 0)..Point::new(7, 0),
12574                    primary: None,
12575                },
12576                ExcerptRange {
12577                    context: Point::new(9, 0)..Point::new(10, 3),
12578                    primary: None,
12579                },
12580            ],
12581            cx,
12582        );
12583        multibuffer.push_excerpts(
12584            buffer_3.clone(),
12585            [
12586                ExcerptRange {
12587                    context: Point::new(0, 0)..Point::new(3, 0),
12588                    primary: None,
12589                },
12590                ExcerptRange {
12591                    context: Point::new(5, 0)..Point::new(7, 0),
12592                    primary: None,
12593                },
12594                ExcerptRange {
12595                    context: Point::new(9, 0)..Point::new(10, 3),
12596                    primary: None,
12597                },
12598            ],
12599            cx,
12600        );
12601        multibuffer
12602    });
12603
12604    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12605    editor
12606        .update(cx, |editor, cx| {
12607            for (buffer, diff_base) in [
12608                (buffer_1.clone(), file_1_old),
12609                (buffer_2.clone(), file_2_old),
12610                (buffer_3.clone(), file_3_old),
12611            ] {
12612                let change_set = cx.new_model(|cx| {
12613                    BufferChangeSet::new_with_base_text(
12614                        diff_base.to_string(),
12615                        buffer.read(cx).text_snapshot(),
12616                        cx,
12617                    )
12618                });
12619                editor.diff_map.add_change_set(change_set, cx)
12620            }
12621        })
12622        .unwrap();
12623
12624    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12625    cx.run_until_parked();
12626
12627    cx.assert_editor_state(
12628        &"
12629            ˇaaa
12630            ccc
12631            ddd
12632
12633            ggg
12634            hhh
12635
12636
12637            lll
12638            mmm
12639            NNN
12640
12641            qqq
12642            rrr
12643
12644            uuu
12645            111
12646            222
12647            333
12648
12649            666
12650            777
12651
12652            000
12653            !!!"
12654        .unindent(),
12655    );
12656
12657    cx.update_editor(|editor, cx| {
12658        editor.select_all(&SelectAll, cx);
12659        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12660    });
12661    cx.executor().run_until_parked();
12662
12663    cx.assert_state_with_diff(
12664        "
12665            «aaa
12666          - bbb
12667            ccc
12668            ddd
12669
12670            ggg
12671            hhh
12672
12673
12674            lll
12675            mmm
12676          - nnn
12677          + NNN
12678
12679            qqq
12680            rrr
12681
12682            uuu
12683            111
12684            222
12685            333
12686
12687          + 666
12688            777
12689
12690            000
12691            !!!ˇ»"
12692            .unindent(),
12693    );
12694}
12695
12696#[gpui::test]
12697async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12698    init_test(cx, |_| {});
12699
12700    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12701    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12702
12703    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12704    let multi_buffer = cx.new_model(|cx| {
12705        let mut multibuffer = MultiBuffer::new(ReadWrite);
12706        multibuffer.push_excerpts(
12707            buffer.clone(),
12708            [
12709                ExcerptRange {
12710                    context: Point::new(0, 0)..Point::new(2, 0),
12711                    primary: None,
12712                },
12713                ExcerptRange {
12714                    context: Point::new(5, 0)..Point::new(7, 0),
12715                    primary: None,
12716                },
12717            ],
12718            cx,
12719        );
12720        multibuffer
12721    });
12722
12723    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12724    editor
12725        .update(cx, |editor, cx| {
12726            let buffer = buffer.read(cx).text_snapshot();
12727            let change_set = cx
12728                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12729            editor.diff_map.add_change_set(change_set, cx)
12730        })
12731        .unwrap();
12732
12733    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12734    cx.run_until_parked();
12735
12736    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12737    cx.executor().run_until_parked();
12738
12739    cx.assert_state_with_diff(
12740        "
12741            ˇaaa
12742          - bbb
12743          + BBB
12744
12745          - ddd
12746          - eee
12747          + EEE
12748            fff
12749        "
12750        .unindent(),
12751    );
12752}
12753
12754#[gpui::test]
12755async fn test_edits_around_expanded_insertion_hunks(
12756    executor: BackgroundExecutor,
12757    cx: &mut gpui::TestAppContext,
12758) {
12759    init_test(cx, |_| {});
12760
12761    let mut cx = EditorTestContext::new(cx).await;
12762
12763    let diff_base = r#"
12764        use some::mod1;
12765        use some::mod2;
12766
12767        const A: u32 = 42;
12768
12769        fn main() {
12770            println!("hello");
12771
12772            println!("world");
12773        }
12774        "#
12775    .unindent();
12776    executor.run_until_parked();
12777    cx.set_state(
12778        &r#"
12779        use some::mod1;
12780        use some::mod2;
12781
12782        const A: u32 = 42;
12783        const B: u32 = 42;
12784        const C: u32 = 42;
12785        ˇ
12786
12787        fn main() {
12788            println!("hello");
12789
12790            println!("world");
12791        }
12792        "#
12793        .unindent(),
12794    );
12795
12796    cx.set_diff_base(&diff_base);
12797    executor.run_until_parked();
12798
12799    cx.update_editor(|editor, cx| {
12800        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12801    });
12802    executor.run_until_parked();
12803
12804    cx.assert_state_with_diff(
12805        r#"
12806        use some::mod1;
12807        use some::mod2;
12808
12809        const A: u32 = 42;
12810      + const B: u32 = 42;
12811      + const C: u32 = 42;
12812      + ˇ
12813
12814        fn main() {
12815            println!("hello");
12816
12817            println!("world");
12818        }
12819        "#
12820        .unindent(),
12821    );
12822
12823    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12824    executor.run_until_parked();
12825
12826    cx.assert_state_with_diff(
12827        r#"
12828        use some::mod1;
12829        use some::mod2;
12830
12831        const A: u32 = 42;
12832      + const B: u32 = 42;
12833      + const C: u32 = 42;
12834      + const D: u32 = 42;
12835      + ˇ
12836
12837        fn main() {
12838            println!("hello");
12839
12840            println!("world");
12841        }
12842        "#
12843        .unindent(),
12844    );
12845
12846    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12847    executor.run_until_parked();
12848
12849    cx.assert_state_with_diff(
12850        r#"
12851        use some::mod1;
12852        use some::mod2;
12853
12854        const A: u32 = 42;
12855      + const B: u32 = 42;
12856      + const C: u32 = 42;
12857      + const D: u32 = 42;
12858      + const E: u32 = 42;
12859      + ˇ
12860
12861        fn main() {
12862            println!("hello");
12863
12864            println!("world");
12865        }
12866        "#
12867        .unindent(),
12868    );
12869
12870    cx.update_editor(|editor, cx| {
12871        editor.delete_line(&DeleteLine, cx);
12872    });
12873    executor.run_until_parked();
12874
12875    cx.assert_state_with_diff(
12876        r#"
12877        use some::mod1;
12878        use some::mod2;
12879
12880        const A: u32 = 42;
12881      + const B: u32 = 42;
12882      + const C: u32 = 42;
12883      + const D: u32 = 42;
12884      + const E: u32 = 42;
12885        ˇ
12886        fn main() {
12887            println!("hello");
12888
12889            println!("world");
12890        }
12891        "#
12892        .unindent(),
12893    );
12894
12895    cx.update_editor(|editor, cx| {
12896        editor.move_up(&MoveUp, cx);
12897        editor.delete_line(&DeleteLine, cx);
12898        editor.move_up(&MoveUp, cx);
12899        editor.delete_line(&DeleteLine, cx);
12900        editor.move_up(&MoveUp, cx);
12901        editor.delete_line(&DeleteLine, cx);
12902    });
12903    executor.run_until_parked();
12904    cx.assert_state_with_diff(
12905        r#"
12906        use some::mod1;
12907        use some::mod2;
12908
12909        const A: u32 = 42;
12910      + const B: u32 = 42;
12911        ˇ
12912        fn main() {
12913            println!("hello");
12914
12915            println!("world");
12916        }
12917        "#
12918        .unindent(),
12919    );
12920
12921    cx.update_editor(|editor, cx| {
12922        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12923        editor.delete_line(&DeleteLine, cx);
12924    });
12925    executor.run_until_parked();
12926    cx.assert_state_with_diff(
12927        r#"
12928        use some::mod1;
12929      - use some::mod2;
12930      -
12931      - const A: u32 = 42;
12932        ˇ
12933        fn main() {
12934            println!("hello");
12935
12936            println!("world");
12937        }
12938        "#
12939        .unindent(),
12940    );
12941}
12942
12943#[gpui::test]
12944async fn test_edits_around_expanded_deletion_hunks(
12945    executor: BackgroundExecutor,
12946    cx: &mut gpui::TestAppContext,
12947) {
12948    init_test(cx, |_| {});
12949
12950    let mut cx = EditorTestContext::new(cx).await;
12951
12952    let diff_base = r#"
12953        use some::mod1;
12954        use some::mod2;
12955
12956        const A: u32 = 42;
12957        const B: u32 = 42;
12958        const C: u32 = 42;
12959
12960
12961        fn main() {
12962            println!("hello");
12963
12964            println!("world");
12965        }
12966    "#
12967    .unindent();
12968    executor.run_until_parked();
12969    cx.set_state(
12970        &r#"
12971        use some::mod1;
12972        use some::mod2;
12973
12974        ˇconst B: u32 = 42;
12975        const C: u32 = 42;
12976
12977
12978        fn main() {
12979            println!("hello");
12980
12981            println!("world");
12982        }
12983        "#
12984        .unindent(),
12985    );
12986
12987    cx.set_diff_base(&diff_base);
12988    executor.run_until_parked();
12989
12990    cx.update_editor(|editor, cx| {
12991        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12992    });
12993    executor.run_until_parked();
12994
12995    cx.assert_state_with_diff(
12996        r#"
12997        use some::mod1;
12998        use some::mod2;
12999
13000      - const A: u32 = 42;
13001        ˇconst B: u32 = 42;
13002        const C: u32 = 42;
13003
13004
13005        fn main() {
13006            println!("hello");
13007
13008            println!("world");
13009        }
13010        "#
13011        .unindent(),
13012    );
13013
13014    cx.update_editor(|editor, cx| {
13015        editor.delete_line(&DeleteLine, cx);
13016    });
13017    executor.run_until_parked();
13018    cx.assert_state_with_diff(
13019        r#"
13020        use some::mod1;
13021        use some::mod2;
13022
13023      - const A: u32 = 42;
13024      - const B: u32 = 42;
13025        ˇconst C: u32 = 42;
13026
13027
13028        fn main() {
13029            println!("hello");
13030
13031            println!("world");
13032        }
13033        "#
13034        .unindent(),
13035    );
13036
13037    cx.update_editor(|editor, cx| {
13038        editor.delete_line(&DeleteLine, cx);
13039    });
13040    executor.run_until_parked();
13041    cx.assert_state_with_diff(
13042        r#"
13043        use some::mod1;
13044        use some::mod2;
13045
13046      - const A: u32 = 42;
13047      - const B: u32 = 42;
13048      - const C: u32 = 42;
13049        ˇ
13050
13051        fn main() {
13052            println!("hello");
13053
13054            println!("world");
13055        }
13056        "#
13057        .unindent(),
13058    );
13059
13060    cx.update_editor(|editor, cx| {
13061        editor.handle_input("replacement", cx);
13062    });
13063    executor.run_until_parked();
13064    cx.assert_state_with_diff(
13065        r#"
13066        use some::mod1;
13067        use some::mod2;
13068
13069      - const A: u32 = 42;
13070      - const B: u32 = 42;
13071      - const C: u32 = 42;
13072      -
13073      + replacementˇ
13074
13075        fn main() {
13076            println!("hello");
13077
13078            println!("world");
13079        }
13080        "#
13081        .unindent(),
13082    );
13083}
13084
13085#[gpui::test]
13086async fn test_edit_after_expanded_modification_hunk(
13087    executor: BackgroundExecutor,
13088    cx: &mut gpui::TestAppContext,
13089) {
13090    init_test(cx, |_| {});
13091
13092    let mut cx = EditorTestContext::new(cx).await;
13093
13094    let diff_base = r#"
13095        use some::mod1;
13096        use some::mod2;
13097
13098        const A: u32 = 42;
13099        const B: u32 = 42;
13100        const C: u32 = 42;
13101        const D: u32 = 42;
13102
13103
13104        fn main() {
13105            println!("hello");
13106
13107            println!("world");
13108        }"#
13109    .unindent();
13110
13111    cx.set_state(
13112        &r#"
13113        use some::mod1;
13114        use some::mod2;
13115
13116        const A: u32 = 42;
13117        const B: u32 = 42;
13118        const C: u32 = 43ˇ
13119        const D: u32 = 42;
13120
13121
13122        fn main() {
13123            println!("hello");
13124
13125            println!("world");
13126        }"#
13127        .unindent(),
13128    );
13129
13130    cx.set_diff_base(&diff_base);
13131    executor.run_until_parked();
13132    cx.update_editor(|editor, cx| {
13133        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13134    });
13135    executor.run_until_parked();
13136
13137    cx.assert_state_with_diff(
13138        r#"
13139        use some::mod1;
13140        use some::mod2;
13141
13142        const A: u32 = 42;
13143        const B: u32 = 42;
13144      - const C: u32 = 42;
13145      + const C: u32 = 43ˇ
13146        const D: u32 = 42;
13147
13148
13149        fn main() {
13150            println!("hello");
13151
13152            println!("world");
13153        }"#
13154        .unindent(),
13155    );
13156
13157    cx.update_editor(|editor, cx| {
13158        editor.handle_input("\nnew_line\n", cx);
13159    });
13160    executor.run_until_parked();
13161
13162    cx.assert_state_with_diff(
13163        r#"
13164        use some::mod1;
13165        use some::mod2;
13166
13167        const A: u32 = 42;
13168        const B: u32 = 42;
13169      - const C: u32 = 42;
13170      + const C: u32 = 43
13171      + new_line
13172      + ˇ
13173        const D: u32 = 42;
13174
13175
13176        fn main() {
13177            println!("hello");
13178
13179            println!("world");
13180        }"#
13181        .unindent(),
13182    );
13183}
13184
13185async fn setup_indent_guides_editor(
13186    text: &str,
13187    cx: &mut gpui::TestAppContext,
13188) -> (BufferId, EditorTestContext) {
13189    init_test(cx, |_| {});
13190
13191    let mut cx = EditorTestContext::new(cx).await;
13192
13193    let buffer_id = cx.update_editor(|editor, cx| {
13194        editor.set_text(text, cx);
13195        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13196
13197        buffer_ids[0]
13198    });
13199
13200    (buffer_id, cx)
13201}
13202
13203fn assert_indent_guides(
13204    range: Range<u32>,
13205    expected: Vec<IndentGuide>,
13206    active_indices: Option<Vec<usize>>,
13207    cx: &mut EditorTestContext,
13208) {
13209    let indent_guides = cx.update_editor(|editor, cx| {
13210        let snapshot = editor.snapshot(cx).display_snapshot;
13211        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13212            editor,
13213            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13214            true,
13215            &snapshot,
13216            cx,
13217        );
13218
13219        indent_guides.sort_by(|a, b| {
13220            a.depth.cmp(&b.depth).then(
13221                a.start_row
13222                    .cmp(&b.start_row)
13223                    .then(a.end_row.cmp(&b.end_row)),
13224            )
13225        });
13226        indent_guides
13227    });
13228
13229    if let Some(expected) = active_indices {
13230        let active_indices = cx.update_editor(|editor, cx| {
13231            let snapshot = editor.snapshot(cx).display_snapshot;
13232            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13233        });
13234
13235        assert_eq!(
13236            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13237            expected,
13238            "Active indent guide indices do not match"
13239        );
13240    }
13241
13242    let expected: Vec<_> = expected
13243        .into_iter()
13244        .map(|guide| MultiBufferIndentGuide {
13245            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13246            buffer: guide,
13247        })
13248        .collect();
13249
13250    assert_eq!(indent_guides, expected, "Indent guides do not match");
13251}
13252
13253fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13254    IndentGuide {
13255        buffer_id,
13256        start_row,
13257        end_row,
13258        depth,
13259        tab_size: 4,
13260        settings: IndentGuideSettings {
13261            enabled: true,
13262            line_width: 1,
13263            active_line_width: 1,
13264            ..Default::default()
13265        },
13266    }
13267}
13268
13269#[gpui::test]
13270async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13271    let (buffer_id, mut cx) = setup_indent_guides_editor(
13272        &"
13273    fn main() {
13274        let a = 1;
13275    }"
13276        .unindent(),
13277        cx,
13278    )
13279    .await;
13280
13281    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13282}
13283
13284#[gpui::test]
13285async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13286    let (buffer_id, mut cx) = setup_indent_guides_editor(
13287        &"
13288    fn main() {
13289        let a = 1;
13290        let b = 2;
13291    }"
13292        .unindent(),
13293        cx,
13294    )
13295    .await;
13296
13297    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13298}
13299
13300#[gpui::test]
13301async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13302    let (buffer_id, mut cx) = setup_indent_guides_editor(
13303        &"
13304    fn main() {
13305        let a = 1;
13306        if a == 3 {
13307            let b = 2;
13308        } else {
13309            let c = 3;
13310        }
13311    }"
13312        .unindent(),
13313        cx,
13314    )
13315    .await;
13316
13317    assert_indent_guides(
13318        0..8,
13319        vec![
13320            indent_guide(buffer_id, 1, 6, 0),
13321            indent_guide(buffer_id, 3, 3, 1),
13322            indent_guide(buffer_id, 5, 5, 1),
13323        ],
13324        None,
13325        &mut cx,
13326    );
13327}
13328
13329#[gpui::test]
13330async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13331    let (buffer_id, mut cx) = setup_indent_guides_editor(
13332        &"
13333    fn main() {
13334        let a = 1;
13335            let b = 2;
13336        let c = 3;
13337    }"
13338        .unindent(),
13339        cx,
13340    )
13341    .await;
13342
13343    assert_indent_guides(
13344        0..5,
13345        vec![
13346            indent_guide(buffer_id, 1, 3, 0),
13347            indent_guide(buffer_id, 2, 2, 1),
13348        ],
13349        None,
13350        &mut cx,
13351    );
13352}
13353
13354#[gpui::test]
13355async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13356    let (buffer_id, mut cx) = setup_indent_guides_editor(
13357        &"
13358        fn main() {
13359            let a = 1;
13360
13361            let c = 3;
13362        }"
13363        .unindent(),
13364        cx,
13365    )
13366    .await;
13367
13368    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13369}
13370
13371#[gpui::test]
13372async fn test_indent_guide_complex(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        0..11,
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_starts_off_screen(cx: &mut gpui::TestAppContext) {
13405    let (buffer_id, mut cx) = setup_indent_guides_editor(
13406        &"
13407        fn main() {
13408            let a = 1;
13409
13410            let c = 3;
13411
13412            if a == 3 {
13413                let b = 2;
13414            } else {
13415                let c = 3;
13416            }
13417        }"
13418        .unindent(),
13419        cx,
13420    )
13421    .await;
13422
13423    assert_indent_guides(
13424        1..11,
13425        vec![
13426            indent_guide(buffer_id, 1, 9, 0),
13427            indent_guide(buffer_id, 6, 6, 1),
13428            indent_guide(buffer_id, 8, 8, 1),
13429        ],
13430        None,
13431        &mut cx,
13432    );
13433}
13434
13435#[gpui::test]
13436async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13437    let (buffer_id, mut cx) = setup_indent_guides_editor(
13438        &"
13439        fn main() {
13440            let a = 1;
13441
13442            let c = 3;
13443
13444            if a == 3 {
13445                let b = 2;
13446            } else {
13447                let c = 3;
13448            }
13449        }"
13450        .unindent(),
13451        cx,
13452    )
13453    .await;
13454
13455    assert_indent_guides(
13456        1..10,
13457        vec![
13458            indent_guide(buffer_id, 1, 9, 0),
13459            indent_guide(buffer_id, 6, 6, 1),
13460            indent_guide(buffer_id, 8, 8, 1),
13461        ],
13462        None,
13463        &mut cx,
13464    );
13465}
13466
13467#[gpui::test]
13468async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13469    let (buffer_id, mut cx) = setup_indent_guides_editor(
13470        &"
13471        block1
13472            block2
13473                block3
13474                    block4
13475            block2
13476        block1
13477        block1"
13478            .unindent(),
13479        cx,
13480    )
13481    .await;
13482
13483    assert_indent_guides(
13484        1..10,
13485        vec![
13486            indent_guide(buffer_id, 1, 4, 0),
13487            indent_guide(buffer_id, 2, 3, 1),
13488            indent_guide(buffer_id, 3, 3, 2),
13489        ],
13490        None,
13491        &mut cx,
13492    );
13493}
13494
13495#[gpui::test]
13496async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13497    let (buffer_id, mut cx) = setup_indent_guides_editor(
13498        &"
13499        block1
13500            block2
13501                block3
13502
13503        block1
13504        block1"
13505            .unindent(),
13506        cx,
13507    )
13508    .await;
13509
13510    assert_indent_guides(
13511        0..6,
13512        vec![
13513            indent_guide(buffer_id, 1, 2, 0),
13514            indent_guide(buffer_id, 2, 2, 1),
13515        ],
13516        None,
13517        &mut cx,
13518    );
13519}
13520
13521#[gpui::test]
13522async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13523    let (buffer_id, mut cx) = setup_indent_guides_editor(
13524        &"
13525        block1
13526
13527
13528
13529            block2
13530        "
13531        .unindent(),
13532        cx,
13533    )
13534    .await;
13535
13536    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13537}
13538
13539#[gpui::test]
13540async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13541    let (buffer_id, mut cx) = setup_indent_guides_editor(
13542        &"
13543        def a:
13544        \tb = 3
13545        \tif True:
13546        \t\tc = 4
13547        \t\td = 5
13548        \tprint(b)
13549        "
13550        .unindent(),
13551        cx,
13552    )
13553    .await;
13554
13555    assert_indent_guides(
13556        0..6,
13557        vec![
13558            indent_guide(buffer_id, 1, 6, 0),
13559            indent_guide(buffer_id, 3, 4, 1),
13560        ],
13561        None,
13562        &mut cx,
13563    );
13564}
13565
13566#[gpui::test]
13567async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13568    let (buffer_id, mut cx) = setup_indent_guides_editor(
13569        &"
13570    fn main() {
13571        let a = 1;
13572    }"
13573        .unindent(),
13574        cx,
13575    )
13576    .await;
13577
13578    cx.update_editor(|editor, cx| {
13579        editor.change_selections(None, cx, |s| {
13580            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13581        });
13582    });
13583
13584    assert_indent_guides(
13585        0..3,
13586        vec![indent_guide(buffer_id, 1, 1, 0)],
13587        Some(vec![0]),
13588        &mut cx,
13589    );
13590}
13591
13592#[gpui::test]
13593async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13594    let (buffer_id, mut cx) = setup_indent_guides_editor(
13595        &"
13596    fn main() {
13597        if 1 == 2 {
13598            let a = 1;
13599        }
13600    }"
13601        .unindent(),
13602        cx,
13603    )
13604    .await;
13605
13606    cx.update_editor(|editor, cx| {
13607        editor.change_selections(None, cx, |s| {
13608            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13609        });
13610    });
13611
13612    assert_indent_guides(
13613        0..4,
13614        vec![
13615            indent_guide(buffer_id, 1, 3, 0),
13616            indent_guide(buffer_id, 2, 2, 1),
13617        ],
13618        Some(vec![1]),
13619        &mut cx,
13620    );
13621
13622    cx.update_editor(|editor, cx| {
13623        editor.change_selections(None, cx, |s| {
13624            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13625        });
13626    });
13627
13628    assert_indent_guides(
13629        0..4,
13630        vec![
13631            indent_guide(buffer_id, 1, 3, 0),
13632            indent_guide(buffer_id, 2, 2, 1),
13633        ],
13634        Some(vec![1]),
13635        &mut cx,
13636    );
13637
13638    cx.update_editor(|editor, cx| {
13639        editor.change_selections(None, cx, |s| {
13640            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13641        });
13642    });
13643
13644    assert_indent_guides(
13645        0..4,
13646        vec![
13647            indent_guide(buffer_id, 1, 3, 0),
13648            indent_guide(buffer_id, 2, 2, 1),
13649        ],
13650        Some(vec![0]),
13651        &mut cx,
13652    );
13653}
13654
13655#[gpui::test]
13656async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13657    let (buffer_id, mut cx) = setup_indent_guides_editor(
13658        &"
13659    fn main() {
13660        let a = 1;
13661
13662        let b = 2;
13663    }"
13664        .unindent(),
13665        cx,
13666    )
13667    .await;
13668
13669    cx.update_editor(|editor, cx| {
13670        editor.change_selections(None, cx, |s| {
13671            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13672        });
13673    });
13674
13675    assert_indent_guides(
13676        0..5,
13677        vec![indent_guide(buffer_id, 1, 3, 0)],
13678        Some(vec![0]),
13679        &mut cx,
13680    );
13681}
13682
13683#[gpui::test]
13684async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13685    let (buffer_id, mut cx) = setup_indent_guides_editor(
13686        &"
13687    def m:
13688        a = 1
13689        pass"
13690            .unindent(),
13691        cx,
13692    )
13693    .await;
13694
13695    cx.update_editor(|editor, cx| {
13696        editor.change_selections(None, cx, |s| {
13697            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13698        });
13699    });
13700
13701    assert_indent_guides(
13702        0..3,
13703        vec![indent_guide(buffer_id, 1, 2, 0)],
13704        Some(vec![0]),
13705        &mut cx,
13706    );
13707}
13708
13709#[gpui::test]
13710fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13711    init_test(cx, |_| {});
13712
13713    let editor = cx.add_window(|cx| {
13714        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13715        build_editor(buffer, cx)
13716    });
13717
13718    let render_args = Arc::new(Mutex::new(None));
13719    let snapshot = editor
13720        .update(cx, |editor, cx| {
13721            let snapshot = editor.buffer().read(cx).snapshot(cx);
13722            let range =
13723                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13724
13725            struct RenderArgs {
13726                row: MultiBufferRow,
13727                folded: bool,
13728                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13729            }
13730
13731            let crease = Crease::inline(
13732                range,
13733                FoldPlaceholder::test(),
13734                {
13735                    let toggle_callback = render_args.clone();
13736                    move |row, folded, callback, _cx| {
13737                        *toggle_callback.lock() = Some(RenderArgs {
13738                            row,
13739                            folded,
13740                            callback,
13741                        });
13742                        div()
13743                    }
13744                },
13745                |_row, _folded, _cx| div(),
13746            );
13747
13748            editor.insert_creases(Some(crease), cx);
13749            let snapshot = editor.snapshot(cx);
13750            let _div =
13751                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13752            snapshot
13753        })
13754        .unwrap();
13755
13756    let render_args = render_args.lock().take().unwrap();
13757    assert_eq!(render_args.row, MultiBufferRow(1));
13758    assert!(!render_args.folded);
13759    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13760
13761    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13762        .unwrap();
13763    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13764    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13765
13766    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13767        .unwrap();
13768    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13769    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13770}
13771
13772#[gpui::test]
13773async fn test_input_text(cx: &mut gpui::TestAppContext) {
13774    init_test(cx, |_| {});
13775    let mut cx = EditorTestContext::new(cx).await;
13776
13777    cx.set_state(
13778        &r#"ˇone
13779        two
13780
13781        three
13782        fourˇ
13783        five
13784
13785        siˇx"#
13786            .unindent(),
13787    );
13788
13789    cx.dispatch_action(HandleInput(String::new()));
13790    cx.assert_editor_state(
13791        &r#"ˇone
13792        two
13793
13794        three
13795        fourˇ
13796        five
13797
13798        siˇx"#
13799            .unindent(),
13800    );
13801
13802    cx.dispatch_action(HandleInput("AAAA".to_string()));
13803    cx.assert_editor_state(
13804        &r#"AAAAˇone
13805        two
13806
13807        three
13808        fourAAAAˇ
13809        five
13810
13811        siAAAAˇx"#
13812            .unindent(),
13813    );
13814}
13815
13816#[gpui::test]
13817async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13818    init_test(cx, |_| {});
13819
13820    let mut cx = EditorTestContext::new(cx).await;
13821    cx.set_state(
13822        r#"let foo = 1;
13823let foo = 2;
13824let foo = 3;
13825let fooˇ = 4;
13826let foo = 5;
13827let foo = 6;
13828let foo = 7;
13829let foo = 8;
13830let foo = 9;
13831let foo = 10;
13832let foo = 11;
13833let foo = 12;
13834let foo = 13;
13835let foo = 14;
13836let foo = 15;"#,
13837    );
13838
13839    cx.update_editor(|e, cx| {
13840        assert_eq!(
13841            e.next_scroll_position,
13842            NextScrollCursorCenterTopBottom::Center,
13843            "Default next scroll direction is center",
13844        );
13845
13846        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13847        assert_eq!(
13848            e.next_scroll_position,
13849            NextScrollCursorCenterTopBottom::Top,
13850            "After center, next scroll direction should be top",
13851        );
13852
13853        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13854        assert_eq!(
13855            e.next_scroll_position,
13856            NextScrollCursorCenterTopBottom::Bottom,
13857            "After top, next scroll direction should be bottom",
13858        );
13859
13860        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13861        assert_eq!(
13862            e.next_scroll_position,
13863            NextScrollCursorCenterTopBottom::Center,
13864            "After bottom, scrolling should start over",
13865        );
13866
13867        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13868        assert_eq!(
13869            e.next_scroll_position,
13870            NextScrollCursorCenterTopBottom::Top,
13871            "Scrolling continues if retriggered fast enough"
13872        );
13873    });
13874
13875    cx.executor()
13876        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13877    cx.executor().run_until_parked();
13878    cx.update_editor(|e, _| {
13879        assert_eq!(
13880            e.next_scroll_position,
13881            NextScrollCursorCenterTopBottom::Center,
13882            "If scrolling is not triggered fast enough, it should reset"
13883        );
13884    });
13885}
13886
13887#[gpui::test]
13888async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13889    init_test(cx, |_| {});
13890    let mut cx = EditorLspTestContext::new_rust(
13891        lsp::ServerCapabilities {
13892            definition_provider: Some(lsp::OneOf::Left(true)),
13893            references_provider: Some(lsp::OneOf::Left(true)),
13894            ..lsp::ServerCapabilities::default()
13895        },
13896        cx,
13897    )
13898    .await;
13899
13900    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13901        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13902            move |params, _| async move {
13903                if empty_go_to_definition {
13904                    Ok(None)
13905                } else {
13906                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13907                        uri: params.text_document_position_params.text_document.uri,
13908                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13909                    })))
13910                }
13911            },
13912        );
13913        let references =
13914            cx.lsp
13915                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13916                    Ok(Some(vec![lsp::Location {
13917                        uri: params.text_document_position.text_document.uri,
13918                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13919                    }]))
13920                });
13921        (go_to_definition, references)
13922    };
13923
13924    cx.set_state(
13925        &r#"fn one() {
13926            let mut a = ˇtwo();
13927        }
13928
13929        fn two() {}"#
13930            .unindent(),
13931    );
13932    set_up_lsp_handlers(false, &mut cx);
13933    let navigated = cx
13934        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13935        .await
13936        .expect("Failed to navigate to definition");
13937    assert_eq!(
13938        navigated,
13939        Navigated::Yes,
13940        "Should have navigated to definition from the GetDefinition response"
13941    );
13942    cx.assert_editor_state(
13943        &r#"fn one() {
13944            let mut a = two();
13945        }
13946
13947        fn «twoˇ»() {}"#
13948            .unindent(),
13949    );
13950
13951    let editors = cx.update_workspace(|workspace, cx| {
13952        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13953    });
13954    cx.update_editor(|_, test_editor_cx| {
13955        assert_eq!(
13956            editors.len(),
13957            1,
13958            "Initially, only one, test, editor should be open in the workspace"
13959        );
13960        assert_eq!(
13961            test_editor_cx.view(),
13962            editors.last().expect("Asserted len is 1")
13963        );
13964    });
13965
13966    set_up_lsp_handlers(true, &mut cx);
13967    let navigated = cx
13968        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13969        .await
13970        .expect("Failed to navigate to lookup references");
13971    assert_eq!(
13972        navigated,
13973        Navigated::Yes,
13974        "Should have navigated to references as a fallback after empty GoToDefinition response"
13975    );
13976    // We should not change the selections in the existing file,
13977    // if opening another milti buffer with the references
13978    cx.assert_editor_state(
13979        &r#"fn one() {
13980            let mut a = two();
13981        }
13982
13983        fn «twoˇ»() {}"#
13984            .unindent(),
13985    );
13986    let editors = cx.update_workspace(|workspace, cx| {
13987        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13988    });
13989    cx.update_editor(|_, test_editor_cx| {
13990        assert_eq!(
13991            editors.len(),
13992            2,
13993            "After falling back to references search, we open a new editor with the results"
13994        );
13995        let references_fallback_text = editors
13996            .into_iter()
13997            .find(|new_editor| new_editor != test_editor_cx.view())
13998            .expect("Should have one non-test editor now")
13999            .read(test_editor_cx)
14000            .text(test_editor_cx);
14001        assert_eq!(
14002            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14003            "Should use the range from the references response and not the GoToDefinition one"
14004        );
14005    });
14006}
14007
14008#[gpui::test]
14009async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14010    init_test(cx, |_| {});
14011
14012    let language = Arc::new(Language::new(
14013        LanguageConfig::default(),
14014        Some(tree_sitter_rust::LANGUAGE.into()),
14015    ));
14016
14017    let text = r#"
14018        #[cfg(test)]
14019        mod tests() {
14020            #[test]
14021            fn runnable_1() {
14022                let a = 1;
14023            }
14024
14025            #[test]
14026            fn runnable_2() {
14027                let a = 1;
14028                let b = 2;
14029            }
14030        }
14031    "#
14032    .unindent();
14033
14034    let fs = FakeFs::new(cx.executor());
14035    fs.insert_file("/file.rs", Default::default()).await;
14036
14037    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14038    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14039    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14040    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
14041    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14042
14043    let editor = cx.new_view(|cx| {
14044        Editor::new(
14045            EditorMode::Full,
14046            multi_buffer,
14047            Some(project.clone()),
14048            true,
14049            cx,
14050        )
14051    });
14052
14053    editor.update(cx, |editor, cx| {
14054        editor.tasks.insert(
14055            (buffer.read(cx).remote_id(), 3),
14056            RunnableTasks {
14057                templates: vec![],
14058                offset: MultiBufferOffset(43),
14059                column: 0,
14060                extra_variables: HashMap::default(),
14061                context_range: BufferOffset(43)..BufferOffset(85),
14062            },
14063        );
14064        editor.tasks.insert(
14065            (buffer.read(cx).remote_id(), 8),
14066            RunnableTasks {
14067                templates: vec![],
14068                offset: MultiBufferOffset(86),
14069                column: 0,
14070                extra_variables: HashMap::default(),
14071                context_range: BufferOffset(86)..BufferOffset(191),
14072            },
14073        );
14074
14075        // Test finding task when cursor is inside function body
14076        editor.change_selections(None, cx, |s| {
14077            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14078        });
14079        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14080        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14081
14082        // Test finding task when cursor is on function name
14083        editor.change_selections(None, cx, |s| {
14084            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14085        });
14086        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14087        assert_eq!(row, 8, "Should find task when cursor is on function name");
14088    });
14089}
14090
14091#[gpui::test]
14092async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14093    init_test(cx, |_| {});
14094
14095    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14096    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14097    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14098
14099    let fs = FakeFs::new(cx.executor());
14100    fs.insert_tree(
14101        "/a",
14102        json!({
14103            "first.rs": sample_text_1,
14104            "second.rs": sample_text_2,
14105            "third.rs": sample_text_3,
14106        }),
14107    )
14108    .await;
14109    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14110    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14111    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14112    let worktree = project.update(cx, |project, cx| {
14113        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14114        assert_eq!(worktrees.len(), 1);
14115        worktrees.pop().unwrap()
14116    });
14117    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14118
14119    let buffer_1 = project
14120        .update(cx, |project, cx| {
14121            project.open_buffer((worktree_id, "first.rs"), cx)
14122        })
14123        .await
14124        .unwrap();
14125    let buffer_2 = project
14126        .update(cx, |project, cx| {
14127            project.open_buffer((worktree_id, "second.rs"), cx)
14128        })
14129        .await
14130        .unwrap();
14131    let buffer_3 = project
14132        .update(cx, |project, cx| {
14133            project.open_buffer((worktree_id, "third.rs"), cx)
14134        })
14135        .await
14136        .unwrap();
14137
14138    let multi_buffer = cx.new_model(|cx| {
14139        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14140        multi_buffer.push_excerpts(
14141            buffer_1.clone(),
14142            [
14143                ExcerptRange {
14144                    context: Point::new(0, 0)..Point::new(3, 0),
14145                    primary: None,
14146                },
14147                ExcerptRange {
14148                    context: Point::new(5, 0)..Point::new(7, 0),
14149                    primary: None,
14150                },
14151                ExcerptRange {
14152                    context: Point::new(9, 0)..Point::new(10, 4),
14153                    primary: None,
14154                },
14155            ],
14156            cx,
14157        );
14158        multi_buffer.push_excerpts(
14159            buffer_2.clone(),
14160            [
14161                ExcerptRange {
14162                    context: Point::new(0, 0)..Point::new(3, 0),
14163                    primary: None,
14164                },
14165                ExcerptRange {
14166                    context: Point::new(5, 0)..Point::new(7, 0),
14167                    primary: None,
14168                },
14169                ExcerptRange {
14170                    context: Point::new(9, 0)..Point::new(10, 4),
14171                    primary: None,
14172                },
14173            ],
14174            cx,
14175        );
14176        multi_buffer.push_excerpts(
14177            buffer_3.clone(),
14178            [
14179                ExcerptRange {
14180                    context: Point::new(0, 0)..Point::new(3, 0),
14181                    primary: None,
14182                },
14183                ExcerptRange {
14184                    context: Point::new(5, 0)..Point::new(7, 0),
14185                    primary: None,
14186                },
14187                ExcerptRange {
14188                    context: Point::new(9, 0)..Point::new(10, 4),
14189                    primary: None,
14190                },
14191            ],
14192            cx,
14193        );
14194        multi_buffer
14195    });
14196    let multi_buffer_editor = cx.new_view(|cx| {
14197        Editor::new(
14198            EditorMode::Full,
14199            multi_buffer,
14200            Some(project.clone()),
14201            true,
14202            cx,
14203        )
14204    });
14205
14206    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
14207    assert_eq!(
14208        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14209        full_text,
14210    );
14211
14212    multi_buffer_editor.update(cx, |editor, cx| {
14213        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14214    });
14215    assert_eq!(
14216        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14217        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14218        "After folding the first buffer, its text should not be displayed"
14219    );
14220
14221    multi_buffer_editor.update(cx, |editor, cx| {
14222        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14223    });
14224    assert_eq!(
14225        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14226        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14227        "After folding the second buffer, its text should not be displayed"
14228    );
14229
14230    multi_buffer_editor.update(cx, |editor, cx| {
14231        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14232    });
14233    assert_eq!(
14234        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14235        "\n\n\n\n\n",
14236        "After folding the third buffer, its text should not be displayed"
14237    );
14238
14239    // Emulate selection inside the fold logic, that should work
14240    multi_buffer_editor.update(cx, |editor, cx| {
14241        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14242    });
14243
14244    multi_buffer_editor.update(cx, |editor, cx| {
14245        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14246    });
14247    assert_eq!(
14248        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14249        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14250        "After unfolding the second buffer, its text should be displayed"
14251    );
14252
14253    multi_buffer_editor.update(cx, |editor, cx| {
14254        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14255    });
14256    assert_eq!(
14257        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14258        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14259        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14260    );
14261
14262    multi_buffer_editor.update(cx, |editor, cx| {
14263        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14264    });
14265    assert_eq!(
14266        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14267        full_text,
14268        "After unfolding the all buffers, all original text should be displayed"
14269    );
14270}
14271
14272#[gpui::test]
14273async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14274    init_test(cx, |_| {});
14275
14276    let sample_text_1 = "1111\n2222\n3333".to_string();
14277    let sample_text_2 = "4444\n5555\n6666".to_string();
14278    let sample_text_3 = "7777\n8888\n9999".to_string();
14279
14280    let fs = FakeFs::new(cx.executor());
14281    fs.insert_tree(
14282        "/a",
14283        json!({
14284            "first.rs": sample_text_1,
14285            "second.rs": sample_text_2,
14286            "third.rs": sample_text_3,
14287        }),
14288    )
14289    .await;
14290    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14291    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14292    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14293    let worktree = project.update(cx, |project, cx| {
14294        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14295        assert_eq!(worktrees.len(), 1);
14296        worktrees.pop().unwrap()
14297    });
14298    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14299
14300    let buffer_1 = project
14301        .update(cx, |project, cx| {
14302            project.open_buffer((worktree_id, "first.rs"), cx)
14303        })
14304        .await
14305        .unwrap();
14306    let buffer_2 = project
14307        .update(cx, |project, cx| {
14308            project.open_buffer((worktree_id, "second.rs"), cx)
14309        })
14310        .await
14311        .unwrap();
14312    let buffer_3 = project
14313        .update(cx, |project, cx| {
14314            project.open_buffer((worktree_id, "third.rs"), cx)
14315        })
14316        .await
14317        .unwrap();
14318
14319    let multi_buffer = cx.new_model(|cx| {
14320        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14321        multi_buffer.push_excerpts(
14322            buffer_1.clone(),
14323            [ExcerptRange {
14324                context: Point::new(0, 0)..Point::new(3, 0),
14325                primary: None,
14326            }],
14327            cx,
14328        );
14329        multi_buffer.push_excerpts(
14330            buffer_2.clone(),
14331            [ExcerptRange {
14332                context: Point::new(0, 0)..Point::new(3, 0),
14333                primary: None,
14334            }],
14335            cx,
14336        );
14337        multi_buffer.push_excerpts(
14338            buffer_3.clone(),
14339            [ExcerptRange {
14340                context: Point::new(0, 0)..Point::new(3, 0),
14341                primary: None,
14342            }],
14343            cx,
14344        );
14345        multi_buffer
14346    });
14347
14348    let multi_buffer_editor = cx.new_view(|cx| {
14349        Editor::new(
14350            EditorMode::Full,
14351            multi_buffer,
14352            Some(project.clone()),
14353            true,
14354            cx,
14355        )
14356    });
14357
14358    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14359    assert_eq!(
14360        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14361        full_text,
14362    );
14363
14364    multi_buffer_editor.update(cx, |editor, cx| {
14365        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14366    });
14367    assert_eq!(
14368        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14369        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14370        "After folding the first buffer, its text should not be displayed"
14371    );
14372
14373    multi_buffer_editor.update(cx, |editor, cx| {
14374        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14375    });
14376
14377    assert_eq!(
14378        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14379        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14380        "After folding the second buffer, its text should not be displayed"
14381    );
14382
14383    multi_buffer_editor.update(cx, |editor, cx| {
14384        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14385    });
14386    assert_eq!(
14387        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14388        "\n\n\n\n\n",
14389        "After folding the third buffer, its text should not be displayed"
14390    );
14391
14392    multi_buffer_editor.update(cx, |editor, cx| {
14393        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14394    });
14395    assert_eq!(
14396        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14397        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14398        "After unfolding the second buffer, its text should be displayed"
14399    );
14400
14401    multi_buffer_editor.update(cx, |editor, cx| {
14402        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14403    });
14404    assert_eq!(
14405        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14406        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14407        "After unfolding the first buffer, its text should be displayed"
14408    );
14409
14410    multi_buffer_editor.update(cx, |editor, cx| {
14411        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14412    });
14413    assert_eq!(
14414        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14415        full_text,
14416        "After unfolding all buffers, all original text should be displayed"
14417    );
14418}
14419
14420#[gpui::test]
14421async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14422    init_test(cx, |_| {});
14423
14424    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14425
14426    let fs = FakeFs::new(cx.executor());
14427    fs.insert_tree(
14428        "/a",
14429        json!({
14430            "main.rs": sample_text,
14431        }),
14432    )
14433    .await;
14434    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14435    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14436    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14437    let worktree = project.update(cx, |project, cx| {
14438        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14439        assert_eq!(worktrees.len(), 1);
14440        worktrees.pop().unwrap()
14441    });
14442    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14443
14444    let buffer_1 = project
14445        .update(cx, |project, cx| {
14446            project.open_buffer((worktree_id, "main.rs"), cx)
14447        })
14448        .await
14449        .unwrap();
14450
14451    let multi_buffer = cx.new_model(|cx| {
14452        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14453        multi_buffer.push_excerpts(
14454            buffer_1.clone(),
14455            [ExcerptRange {
14456                context: Point::new(0, 0)
14457                    ..Point::new(
14458                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14459                        0,
14460                    ),
14461                primary: None,
14462            }],
14463            cx,
14464        );
14465        multi_buffer
14466    });
14467    let multi_buffer_editor = cx.new_view(|cx| {
14468        Editor::new(
14469            EditorMode::Full,
14470            multi_buffer,
14471            Some(project.clone()),
14472            true,
14473            cx,
14474        )
14475    });
14476
14477    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14478    multi_buffer_editor.update(cx, |editor, cx| {
14479        enum TestHighlight {}
14480        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14481        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14482        editor.highlight_text::<TestHighlight>(
14483            vec![highlight_range.clone()],
14484            HighlightStyle::color(Hsla::green()),
14485            cx,
14486        );
14487        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14488    });
14489
14490    let full_text = format!("\n\n\n{sample_text}\n");
14491    assert_eq!(
14492        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14493        full_text,
14494    );
14495}
14496
14497#[gpui::test]
14498fn test_inline_completion_text(cx: &mut TestAppContext) {
14499    init_test(cx, |_| {});
14500
14501    // Simple insertion
14502    {
14503        let window = cx.add_window(|cx| {
14504            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14505            Editor::new(EditorMode::Full, buffer, None, true, cx)
14506        });
14507        let cx = &mut VisualTestContext::from_window(*window, cx);
14508
14509        window
14510            .update(cx, |editor, cx| {
14511                let snapshot = editor.snapshot(cx);
14512                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14513                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14514                let edits = vec![(edit_range, " beautiful".to_string())];
14515
14516                let InlineCompletionText::Edit { text, highlights } =
14517                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14518                else {
14519                    panic!("Failed to generate inline completion text");
14520                };
14521
14522                assert_eq!(text, "Hello, beautiful world!");
14523                assert_eq!(highlights.len(), 1);
14524                assert_eq!(highlights[0].0, 6..16);
14525                assert_eq!(
14526                    highlights[0].1.background_color,
14527                    Some(cx.theme().status().created_background)
14528                );
14529            })
14530            .unwrap();
14531    }
14532
14533    // Replacement
14534    {
14535        let window = cx.add_window(|cx| {
14536            let buffer = MultiBuffer::build_simple("This is a test.", cx);
14537            Editor::new(EditorMode::Full, buffer, None, true, cx)
14538        });
14539        let cx = &mut VisualTestContext::from_window(*window, cx);
14540
14541        window
14542            .update(cx, |editor, cx| {
14543                let snapshot = editor.snapshot(cx);
14544                let edits = vec![(
14545                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14546                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
14547                    "That".to_string(),
14548                )];
14549
14550                let InlineCompletionText::Edit { text, highlights } =
14551                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14552                else {
14553                    panic!("Failed to generate inline completion text");
14554                };
14555
14556                assert_eq!(text, "That is a test.");
14557                assert_eq!(highlights.len(), 1);
14558                assert_eq!(highlights[0].0, 0..4);
14559                assert_eq!(
14560                    highlights[0].1.background_color,
14561                    Some(cx.theme().status().created_background)
14562                );
14563            })
14564            .unwrap();
14565    }
14566
14567    // Multiple edits
14568    {
14569        let window = cx.add_window(|cx| {
14570            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14571            Editor::new(EditorMode::Full, buffer, None, true, cx)
14572        });
14573        let cx = &mut VisualTestContext::from_window(*window, cx);
14574
14575        window
14576            .update(cx, |editor, cx| {
14577                let snapshot = editor.snapshot(cx);
14578                let edits = vec![
14579                    (
14580                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14581                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
14582                        "Greetings".into(),
14583                    ),
14584                    (
14585                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
14586                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
14587                        " and universe".into(),
14588                    ),
14589                ];
14590
14591                let InlineCompletionText::Edit { text, highlights } =
14592                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14593                else {
14594                    panic!("Failed to generate inline completion text");
14595                };
14596
14597                assert_eq!(text, "Greetings, world and universe!");
14598                assert_eq!(highlights.len(), 2);
14599                assert_eq!(highlights[0].0, 0..9);
14600                assert_eq!(highlights[1].0, 16..29);
14601                assert_eq!(
14602                    highlights[0].1.background_color,
14603                    Some(cx.theme().status().created_background)
14604                );
14605                assert_eq!(
14606                    highlights[1].1.background_color,
14607                    Some(cx.theme().status().created_background)
14608                );
14609            })
14610            .unwrap();
14611    }
14612
14613    // Multiple lines with edits
14614    {
14615        let window = cx.add_window(|cx| {
14616            let buffer =
14617                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
14618            Editor::new(EditorMode::Full, buffer, None, true, cx)
14619        });
14620        let cx = &mut VisualTestContext::from_window(*window, cx);
14621
14622        window
14623            .update(cx, |editor, cx| {
14624                let snapshot = editor.snapshot(cx);
14625                let edits = vec![
14626                    (
14627                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
14628                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
14629                        "modified".to_string(),
14630                    ),
14631                    (
14632                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
14633                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
14634                        "New third line".to_string(),
14635                    ),
14636                    (
14637                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
14638                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
14639                        " updated".to_string(),
14640                    ),
14641                ];
14642
14643                let InlineCompletionText::Edit { text, highlights } =
14644                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14645                else {
14646                    panic!("Failed to generate inline completion text");
14647                };
14648
14649                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
14650                assert_eq!(highlights.len(), 3);
14651                assert_eq!(highlights[0].0, 7..15); // "modified"
14652                assert_eq!(highlights[1].0, 16..30); // "New third line"
14653                assert_eq!(highlights[2].0, 37..45); // " updated"
14654
14655                for highlight in &highlights {
14656                    assert_eq!(
14657                        highlight.1.background_color,
14658                        Some(cx.theme().status().created_background)
14659                    );
14660                }
14661            })
14662            .unwrap();
14663    }
14664}
14665
14666#[gpui::test]
14667fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
14668    init_test(cx, |_| {});
14669
14670    // Deletion
14671    {
14672        let window = cx.add_window(|cx| {
14673            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14674            Editor::new(EditorMode::Full, buffer, None, true, cx)
14675        });
14676        let cx = &mut VisualTestContext::from_window(*window, cx);
14677
14678        window
14679            .update(cx, |editor, cx| {
14680                let snapshot = editor.snapshot(cx);
14681                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
14682                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
14683                let edits = vec![(edit_range, "".to_string())];
14684
14685                let InlineCompletionText::Edit { text, highlights } =
14686                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14687                else {
14688                    panic!("Failed to generate inline completion text");
14689                };
14690
14691                assert_eq!(text, "Hello, world!");
14692                assert_eq!(highlights.len(), 1);
14693                assert_eq!(highlights[0].0, 5..11);
14694                assert_eq!(
14695                    highlights[0].1.background_color,
14696                    Some(cx.theme().status().deleted_background)
14697                );
14698            })
14699            .unwrap();
14700    }
14701
14702    // Insertion
14703    {
14704        let window = cx.add_window(|cx| {
14705            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14706            Editor::new(EditorMode::Full, buffer, None, true, cx)
14707        });
14708        let cx = &mut VisualTestContext::from_window(*window, cx);
14709
14710        window
14711            .update(cx, |editor, cx| {
14712                let snapshot = editor.snapshot(cx);
14713                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14714                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14715                let edits = vec![(edit_range, " digital".to_string())];
14716
14717                let InlineCompletionText::Edit { text, highlights } =
14718                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14719                else {
14720                    panic!("Failed to generate inline completion text");
14721                };
14722
14723                assert_eq!(text, "Hello, digital world!");
14724                assert_eq!(highlights.len(), 1);
14725                assert_eq!(highlights[0].0, 6..14);
14726                assert_eq!(
14727                    highlights[0].1.background_color,
14728                    Some(cx.theme().status().created_background)
14729                );
14730            })
14731            .unwrap();
14732    }
14733}
14734
14735#[gpui::test]
14736async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
14737    init_test(cx, |_| {});
14738    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14739
14740    cx.set_state(indoc! {"
14741        struct Fˇoo {}
14742    "});
14743
14744    cx.update_editor(|editor, cx| {
14745        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
14746        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
14747        editor.highlight_background::<DocumentHighlightRead>(
14748            &[highlight_range],
14749            |c| c.editor_document_highlight_read_background,
14750            cx,
14751        );
14752    });
14753
14754    cx.update_editor(|e, cx| e.rename(&Rename, cx))
14755        .expect("Rename was not started")
14756        .await
14757        .expect("Rename failed");
14758    let mut rename_handler =
14759        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
14760            let edit = lsp::TextEdit {
14761                range: lsp::Range {
14762                    start: lsp::Position {
14763                        line: 0,
14764                        character: 7,
14765                    },
14766                    end: lsp::Position {
14767                        line: 0,
14768                        character: 10,
14769                    },
14770                },
14771                new_text: "FooRenamed".to_string(),
14772            };
14773            Ok(Some(lsp::WorkspaceEdit::new(
14774                // Specify the same edit twice
14775                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
14776            )))
14777        });
14778    cx.update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
14779        .expect("Confirm rename was not started")
14780        .await
14781        .expect("Confirm rename failed");
14782    rename_handler.next().await.unwrap();
14783    cx.run_until_parked();
14784
14785    // Despite two edits, only one is actually applied as those are identical
14786    cx.assert_editor_state(indoc! {"
14787        struct FooRenamedˇ {}
14788    "});
14789}
14790
14791fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
14792    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
14793    point..point
14794}
14795
14796fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
14797    let (text, ranges) = marked_text_ranges(marked_text, true);
14798    assert_eq!(view.text(cx), text);
14799    assert_eq!(
14800        view.selections.ranges(cx),
14801        ranges,
14802        "Assert selections are {}",
14803        marked_text
14804    );
14805}
14806
14807pub fn handle_signature_help_request(
14808    cx: &mut EditorLspTestContext,
14809    mocked_response: lsp::SignatureHelp,
14810) -> impl Future<Output = ()> {
14811    let mut request =
14812        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
14813            let mocked_response = mocked_response.clone();
14814            async move { Ok(Some(mocked_response)) }
14815        });
14816
14817    async move {
14818        request.next().await;
14819    }
14820}
14821
14822/// Handle completion request passing a marked string specifying where the completion
14823/// should be triggered from using '|' character, what range should be replaced, and what completions
14824/// should be returned using '<' and '>' to delimit the range
14825pub fn handle_completion_request(
14826    cx: &mut EditorLspTestContext,
14827    marked_string: &str,
14828    completions: Vec<&'static str>,
14829    counter: Arc<AtomicUsize>,
14830) -> impl Future<Output = ()> {
14831    let complete_from_marker: TextRangeMarker = '|'.into();
14832    let replace_range_marker: TextRangeMarker = ('<', '>').into();
14833    let (_, mut marked_ranges) = marked_text_ranges_by(
14834        marked_string,
14835        vec![complete_from_marker.clone(), replace_range_marker.clone()],
14836    );
14837
14838    let complete_from_position =
14839        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
14840    let replace_range =
14841        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
14842
14843    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
14844        let completions = completions.clone();
14845        counter.fetch_add(1, atomic::Ordering::Release);
14846        async move {
14847            assert_eq!(params.text_document_position.text_document.uri, url.clone());
14848            assert_eq!(
14849                params.text_document_position.position,
14850                complete_from_position
14851            );
14852            Ok(Some(lsp::CompletionResponse::Array(
14853                completions
14854                    .iter()
14855                    .map(|completion_text| lsp::CompletionItem {
14856                        label: completion_text.to_string(),
14857                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14858                            range: replace_range,
14859                            new_text: completion_text.to_string(),
14860                        })),
14861                        ..Default::default()
14862                    })
14863                    .collect(),
14864            )))
14865        }
14866    });
14867
14868    async move {
14869        request.next().await;
14870    }
14871}
14872
14873fn handle_resolve_completion_request(
14874    cx: &mut EditorLspTestContext,
14875    edits: Option<Vec<(&'static str, &'static str)>>,
14876) -> impl Future<Output = ()> {
14877    let edits = edits.map(|edits| {
14878        edits
14879            .iter()
14880            .map(|(marked_string, new_text)| {
14881                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
14882                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
14883                lsp::TextEdit::new(replace_range, new_text.to_string())
14884            })
14885            .collect::<Vec<_>>()
14886    });
14887
14888    let mut request =
14889        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14890            let edits = edits.clone();
14891            async move {
14892                Ok(lsp::CompletionItem {
14893                    additional_text_edits: edits,
14894                    ..Default::default()
14895                })
14896            }
14897        });
14898
14899    async move {
14900        request.next().await;
14901    }
14902}
14903
14904pub(crate) fn update_test_language_settings(
14905    cx: &mut TestAppContext,
14906    f: impl Fn(&mut AllLanguageSettingsContent),
14907) {
14908    cx.update(|cx| {
14909        SettingsStore::update_global(cx, |store, cx| {
14910            store.update_user_settings::<AllLanguageSettings>(cx, f);
14911        });
14912    });
14913}
14914
14915pub(crate) fn update_test_project_settings(
14916    cx: &mut TestAppContext,
14917    f: impl Fn(&mut ProjectSettings),
14918) {
14919    cx.update(|cx| {
14920        SettingsStore::update_global(cx, |store, cx| {
14921            store.update_user_settings::<ProjectSettings>(cx, f);
14922        });
14923    });
14924}
14925
14926pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14927    cx.update(|cx| {
14928        assets::Assets.load_test_fonts(cx);
14929        let store = SettingsStore::test(cx);
14930        cx.set_global(store);
14931        theme::init(theme::LoadThemes::JustBase, cx);
14932        release_channel::init(SemanticVersion::default(), cx);
14933        client::init_settings(cx);
14934        language::init(cx);
14935        Project::init_settings(cx);
14936        workspace::init_settings(cx);
14937        crate::init(cx);
14938    });
14939
14940    update_test_language_settings(cx, f);
14941}
14942
14943#[track_caller]
14944fn assert_hunk_revert(
14945    not_reverted_text_with_selections: &str,
14946    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14947    expected_reverted_text_with_selections: &str,
14948    base_text: &str,
14949    cx: &mut EditorLspTestContext,
14950) {
14951    cx.set_state(not_reverted_text_with_selections);
14952    cx.set_diff_base(base_text);
14953    cx.executor().run_until_parked();
14954
14955    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14956        let snapshot = editor.snapshot(cx);
14957        let reverted_hunk_statuses = snapshot
14958            .diff_map
14959            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14960            .map(|hunk| hunk_status(&hunk))
14961            .collect::<Vec<_>>();
14962
14963        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14964        reverted_hunk_statuses
14965    });
14966    cx.executor().run_until_parked();
14967    cx.assert_editor_state(expected_reverted_text_with_selections);
14968    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14969}