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) @injection.content
 5966                (#set! injection.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), &["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),
 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) @injection.content
 9072                (#set! injection.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                        .borrow()
11084                        .iter()
11085                        .flat_map(|c| match c {
11086                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11087                            _ => None,
11088                        })
11089                        .collect::<Vec<String>>(),
11090                    items_out
11091                        .iter()
11092                        .map(|completion| completion.label.clone())
11093                        .collect::<Vec<String>>()
11094                );
11095            }
11096            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11097        }
11098    });
11099    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11100    // with 4 from the end.
11101    assert_eq!(
11102        *resolved_items.lock(),
11103        [
11104            &items_out[0..16],
11105            &items_out[items_out.len() - 4..items_out.len()]
11106        ]
11107        .concat()
11108        .iter()
11109        .cloned()
11110        .collect::<Vec<lsp::CompletionItem>>()
11111    );
11112    resolved_items.lock().clear();
11113
11114    cx.update_editor(|editor, cx| {
11115        editor.context_menu_prev(&ContextMenuPrev, cx);
11116    });
11117    cx.run_until_parked();
11118    // Completions that have already been resolved are skipped.
11119    assert_eq!(
11120        *resolved_items.lock(),
11121        items_out[items_out.len() - 16..items_out.len() - 4]
11122            .iter()
11123            .cloned()
11124            .collect::<Vec<lsp::CompletionItem>>()
11125    );
11126    resolved_items.lock().clear();
11127}
11128
11129#[gpui::test]
11130async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11131    init_test(cx, |_| {});
11132
11133    let mut cx = EditorLspTestContext::new(
11134        Language::new(
11135            LanguageConfig {
11136                matcher: LanguageMatcher {
11137                    path_suffixes: vec!["jsx".into()],
11138                    ..Default::default()
11139                },
11140                overrides: [(
11141                    "element".into(),
11142                    LanguageConfigOverride {
11143                        word_characters: Override::Set(['-'].into_iter().collect()),
11144                        ..Default::default()
11145                    },
11146                )]
11147                .into_iter()
11148                .collect(),
11149                ..Default::default()
11150            },
11151            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11152        )
11153        .with_override_query("(jsx_self_closing_element) @element")
11154        .unwrap(),
11155        lsp::ServerCapabilities {
11156            completion_provider: Some(lsp::CompletionOptions {
11157                trigger_characters: Some(vec![":".to_string()]),
11158                ..Default::default()
11159            }),
11160            ..Default::default()
11161        },
11162        cx,
11163    )
11164    .await;
11165
11166    cx.lsp
11167        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11168            Ok(Some(lsp::CompletionResponse::Array(vec![
11169                lsp::CompletionItem {
11170                    label: "bg-blue".into(),
11171                    ..Default::default()
11172                },
11173                lsp::CompletionItem {
11174                    label: "bg-red".into(),
11175                    ..Default::default()
11176                },
11177                lsp::CompletionItem {
11178                    label: "bg-yellow".into(),
11179                    ..Default::default()
11180                },
11181            ])))
11182        });
11183
11184    cx.set_state(r#"<p class="bgˇ" />"#);
11185
11186    // Trigger completion when typing a dash, because the dash is an extra
11187    // word character in the 'element' scope, which contains the cursor.
11188    cx.simulate_keystroke("-");
11189    cx.executor().run_until_parked();
11190    cx.update_editor(|editor, _| {
11191        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11192        {
11193            assert_eq!(
11194                completion_menu_entries(&menu),
11195                &["bg-red", "bg-blue", "bg-yellow"]
11196            );
11197        } else {
11198            panic!("expected completion menu to be open");
11199        }
11200    });
11201
11202    cx.simulate_keystroke("l");
11203    cx.executor().run_until_parked();
11204    cx.update_editor(|editor, _| {
11205        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11206        {
11207            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11208        } else {
11209            panic!("expected completion menu to be open");
11210        }
11211    });
11212
11213    // When filtering completions, consider the character after the '-' to
11214    // be the start of a subword.
11215    cx.set_state(r#"<p class="yelˇ" />"#);
11216    cx.simulate_keystroke("l");
11217    cx.executor().run_until_parked();
11218    cx.update_editor(|editor, _| {
11219        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11220        {
11221            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11222        } else {
11223            panic!("expected completion menu to be open");
11224        }
11225    });
11226}
11227
11228fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11229    let entries = menu.entries.borrow();
11230    entries
11231        .iter()
11232        .flat_map(|e| match e {
11233            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11234            _ => None,
11235        })
11236        .collect()
11237}
11238
11239#[gpui::test]
11240async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11241    init_test(cx, |settings| {
11242        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11243            FormatterList(vec![Formatter::Prettier].into()),
11244        ))
11245    });
11246
11247    let fs = FakeFs::new(cx.executor());
11248    fs.insert_file("/file.ts", Default::default()).await;
11249
11250    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11251    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11252
11253    language_registry.add(Arc::new(Language::new(
11254        LanguageConfig {
11255            name: "TypeScript".into(),
11256            matcher: LanguageMatcher {
11257                path_suffixes: vec!["ts".to_string()],
11258                ..Default::default()
11259            },
11260            ..Default::default()
11261        },
11262        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11263    )));
11264    update_test_language_settings(cx, |settings| {
11265        settings.defaults.prettier = Some(PrettierSettings {
11266            allowed: true,
11267            ..PrettierSettings::default()
11268        });
11269    });
11270
11271    let test_plugin = "test_plugin";
11272    let _ = language_registry.register_fake_lsp(
11273        "TypeScript",
11274        FakeLspAdapter {
11275            prettier_plugins: vec![test_plugin],
11276            ..Default::default()
11277        },
11278    );
11279
11280    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11281    let buffer = project
11282        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11283        .await
11284        .unwrap();
11285
11286    let buffer_text = "one\ntwo\nthree\n";
11287    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11288    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11289    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11290
11291    editor
11292        .update(cx, |editor, cx| {
11293            editor.perform_format(
11294                project.clone(),
11295                FormatTrigger::Manual,
11296                FormatTarget::Buffer,
11297                cx,
11298            )
11299        })
11300        .unwrap()
11301        .await;
11302    assert_eq!(
11303        editor.update(cx, |editor, cx| editor.text(cx)),
11304        buffer_text.to_string() + prettier_format_suffix,
11305        "Test prettier formatting was not applied to the original buffer text",
11306    );
11307
11308    update_test_language_settings(cx, |settings| {
11309        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11310    });
11311    let format = editor.update(cx, |editor, cx| {
11312        editor.perform_format(
11313            project.clone(),
11314            FormatTrigger::Manual,
11315            FormatTarget::Buffer,
11316            cx,
11317        )
11318    });
11319    format.await.unwrap();
11320    assert_eq!(
11321        editor.update(cx, |editor, cx| editor.text(cx)),
11322        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11323        "Autoformatting (via test prettier) was not applied to the original buffer text",
11324    );
11325}
11326
11327#[gpui::test]
11328async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11329    init_test(cx, |_| {});
11330    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11331    let base_text = indoc! {r#"
11332        struct Row;
11333        struct Row1;
11334        struct Row2;
11335
11336        struct Row4;
11337        struct Row5;
11338        struct Row6;
11339
11340        struct Row8;
11341        struct Row9;
11342        struct Row10;"#};
11343
11344    // When addition hunks are not adjacent to carets, no hunk revert is performed
11345    assert_hunk_revert(
11346        indoc! {r#"struct Row;
11347                   struct Row1;
11348                   struct Row1.1;
11349                   struct Row1.2;
11350                   struct Row2;ˇ
11351
11352                   struct Row4;
11353                   struct Row5;
11354                   struct Row6;
11355
11356                   struct Row8;
11357                   ˇstruct Row9;
11358                   struct Row9.1;
11359                   struct Row9.2;
11360                   struct Row9.3;
11361                   struct Row10;"#},
11362        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11363        indoc! {r#"struct Row;
11364                   struct Row1;
11365                   struct Row1.1;
11366                   struct Row1.2;
11367                   struct Row2;ˇ
11368
11369                   struct Row4;
11370                   struct Row5;
11371                   struct Row6;
11372
11373                   struct Row8;
11374                   ˇstruct Row9;
11375                   struct Row9.1;
11376                   struct Row9.2;
11377                   struct Row9.3;
11378                   struct Row10;"#},
11379        base_text,
11380        &mut cx,
11381    );
11382    // Same for selections
11383    assert_hunk_revert(
11384        indoc! {r#"struct Row;
11385                   struct Row1;
11386                   struct Row2;
11387                   struct Row2.1;
11388                   struct Row2.2;
11389                   «ˇ
11390                   struct Row4;
11391                   struct» Row5;
11392                   «struct Row6;
11393                   ˇ»
11394                   struct Row9.1;
11395                   struct Row9.2;
11396                   struct Row9.3;
11397                   struct Row8;
11398                   struct Row9;
11399                   struct Row10;"#},
11400        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11401        indoc! {r#"struct Row;
11402                   struct Row1;
11403                   struct Row2;
11404                   struct Row2.1;
11405                   struct Row2.2;
11406                   «ˇ
11407                   struct Row4;
11408                   struct» Row5;
11409                   «struct Row6;
11410                   ˇ»
11411                   struct Row9.1;
11412                   struct Row9.2;
11413                   struct Row9.3;
11414                   struct Row8;
11415                   struct Row9;
11416                   struct Row10;"#},
11417        base_text,
11418        &mut cx,
11419    );
11420
11421    // When carets and selections intersect the addition hunks, those are reverted.
11422    // Adjacent carets got merged.
11423    assert_hunk_revert(
11424        indoc! {r#"struct Row;
11425                   ˇ// something on the top
11426                   struct Row1;
11427                   struct Row2;
11428                   struct Roˇw3.1;
11429                   struct Row2.2;
11430                   struct Row2.3;ˇ
11431
11432                   struct Row4;
11433                   struct ˇRow5.1;
11434                   struct Row5.2;
11435                   struct «Rowˇ»5.3;
11436                   struct Row5;
11437                   struct Row6;
11438                   ˇ
11439                   struct Row9.1;
11440                   struct «Rowˇ»9.2;
11441                   struct «ˇRow»9.3;
11442                   struct Row8;
11443                   struct Row9;
11444                   «ˇ// something on bottom»
11445                   struct Row10;"#},
11446        vec![
11447            DiffHunkStatus::Added,
11448            DiffHunkStatus::Added,
11449            DiffHunkStatus::Added,
11450            DiffHunkStatus::Added,
11451            DiffHunkStatus::Added,
11452        ],
11453        indoc! {r#"struct Row;
11454                   ˇstruct Row1;
11455                   struct Row2;
11456                   ˇ
11457                   struct Row4;
11458                   ˇstruct Row5;
11459                   struct Row6;
11460                   ˇ
11461                   ˇstruct Row8;
11462                   struct Row9;
11463                   ˇstruct Row10;"#},
11464        base_text,
11465        &mut cx,
11466    );
11467}
11468
11469#[gpui::test]
11470async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11471    init_test(cx, |_| {});
11472    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11473    let base_text = indoc! {r#"
11474        struct Row;
11475        struct Row1;
11476        struct Row2;
11477
11478        struct Row4;
11479        struct Row5;
11480        struct Row6;
11481
11482        struct Row8;
11483        struct Row9;
11484        struct Row10;"#};
11485
11486    // Modification hunks behave the same as the addition ones.
11487    assert_hunk_revert(
11488        indoc! {r#"struct Row;
11489                   struct Row1;
11490                   struct Row33;
11491                   ˇ
11492                   struct Row4;
11493                   struct Row5;
11494                   struct Row6;
11495                   ˇ
11496                   struct Row99;
11497                   struct Row9;
11498                   struct Row10;"#},
11499        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11500        indoc! {r#"struct Row;
11501                   struct Row1;
11502                   struct Row33;
11503                   ˇ
11504                   struct Row4;
11505                   struct Row5;
11506                   struct Row6;
11507                   ˇ
11508                   struct Row99;
11509                   struct Row9;
11510                   struct Row10;"#},
11511        base_text,
11512        &mut cx,
11513    );
11514    assert_hunk_revert(
11515        indoc! {r#"struct Row;
11516                   struct Row1;
11517                   struct Row33;
11518                   «ˇ
11519                   struct Row4;
11520                   struct» Row5;
11521                   «struct Row6;
11522                   ˇ»
11523                   struct Row99;
11524                   struct Row9;
11525                   struct Row10;"#},
11526        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11527        indoc! {r#"struct Row;
11528                   struct Row1;
11529                   struct Row33;
11530                   «ˇ
11531                   struct Row4;
11532                   struct» Row5;
11533                   «struct Row6;
11534                   ˇ»
11535                   struct Row99;
11536                   struct Row9;
11537                   struct Row10;"#},
11538        base_text,
11539        &mut cx,
11540    );
11541
11542    assert_hunk_revert(
11543        indoc! {r#"ˇstruct Row1.1;
11544                   struct Row1;
11545                   «ˇstr»uct Row22;
11546
11547                   struct ˇRow44;
11548                   struct Row5;
11549                   struct «Rˇ»ow66;ˇ
11550
11551                   «struˇ»ct Row88;
11552                   struct Row9;
11553                   struct Row1011;ˇ"#},
11554        vec![
11555            DiffHunkStatus::Modified,
11556            DiffHunkStatus::Modified,
11557            DiffHunkStatus::Modified,
11558            DiffHunkStatus::Modified,
11559            DiffHunkStatus::Modified,
11560            DiffHunkStatus::Modified,
11561        ],
11562        indoc! {r#"struct Row;
11563                   ˇstruct Row1;
11564                   struct Row2;
11565                   ˇ
11566                   struct Row4;
11567                   ˇstruct Row5;
11568                   struct Row6;
11569                   ˇ
11570                   struct Row8;
11571                   ˇstruct Row9;
11572                   struct Row10;ˇ"#},
11573        base_text,
11574        &mut cx,
11575    );
11576}
11577
11578#[gpui::test]
11579async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11580    init_test(cx, |_| {});
11581    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11582    let base_text = indoc! {r#"struct Row;
11583struct Row1;
11584struct Row2;
11585
11586struct Row4;
11587struct Row5;
11588struct Row6;
11589
11590struct Row8;
11591struct Row9;
11592struct Row10;"#};
11593
11594    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11595    assert_hunk_revert(
11596        indoc! {r#"struct Row;
11597                   struct Row2;
11598
11599                   ˇstruct Row4;
11600                   struct Row5;
11601                   struct Row6;
11602                   ˇ
11603                   struct Row8;
11604                   struct Row10;"#},
11605        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11606        indoc! {r#"struct Row;
11607                   struct Row2;
11608
11609                   ˇstruct Row4;
11610                   struct Row5;
11611                   struct Row6;
11612                   ˇ
11613                   struct Row8;
11614                   struct Row10;"#},
11615        base_text,
11616        &mut cx,
11617    );
11618    assert_hunk_revert(
11619        indoc! {r#"struct Row;
11620                   struct Row2;
11621
11622                   «ˇstruct Row4;
11623                   struct» Row5;
11624                   «struct Row6;
11625                   ˇ»
11626                   struct Row8;
11627                   struct Row10;"#},
11628        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11629        indoc! {r#"struct Row;
11630                   struct Row2;
11631
11632                   «ˇstruct Row4;
11633                   struct» Row5;
11634                   «struct Row6;
11635                   ˇ»
11636                   struct Row8;
11637                   struct Row10;"#},
11638        base_text,
11639        &mut cx,
11640    );
11641
11642    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11643    assert_hunk_revert(
11644        indoc! {r#"struct Row;
11645                   ˇstruct Row2;
11646
11647                   struct Row4;
11648                   struct Row5;
11649                   struct Row6;
11650
11651                   struct Row8;ˇ
11652                   struct Row10;"#},
11653        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11654        indoc! {r#"struct Row;
11655                   struct Row1;
11656                   ˇstruct Row2;
11657
11658                   struct Row4;
11659                   struct Row5;
11660                   struct Row6;
11661
11662                   struct Row8;ˇ
11663                   struct Row9;
11664                   struct Row10;"#},
11665        base_text,
11666        &mut cx,
11667    );
11668    assert_hunk_revert(
11669        indoc! {r#"struct Row;
11670                   struct Row2«ˇ;
11671                   struct Row4;
11672                   struct» Row5;
11673                   «struct Row6;
11674
11675                   struct Row8;ˇ»
11676                   struct Row10;"#},
11677        vec![
11678            DiffHunkStatus::Removed,
11679            DiffHunkStatus::Removed,
11680            DiffHunkStatus::Removed,
11681        ],
11682        indoc! {r#"struct Row;
11683                   struct Row1;
11684                   struct Row2«ˇ;
11685
11686                   struct Row4;
11687                   struct» Row5;
11688                   «struct Row6;
11689
11690                   struct Row8;ˇ»
11691                   struct Row9;
11692                   struct Row10;"#},
11693        base_text,
11694        &mut cx,
11695    );
11696}
11697
11698#[gpui::test]
11699async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11700    init_test(cx, |_| {});
11701
11702    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11703    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11704    let base_text_3 =
11705        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11706
11707    let text_1 = edit_first_char_of_every_line(base_text_1);
11708    let text_2 = edit_first_char_of_every_line(base_text_2);
11709    let text_3 = edit_first_char_of_every_line(base_text_3);
11710
11711    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11712    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11713    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11714
11715    let multibuffer = cx.new_model(|cx| {
11716        let mut multibuffer = MultiBuffer::new(ReadWrite);
11717        multibuffer.push_excerpts(
11718            buffer_1.clone(),
11719            [
11720                ExcerptRange {
11721                    context: Point::new(0, 0)..Point::new(3, 0),
11722                    primary: None,
11723                },
11724                ExcerptRange {
11725                    context: Point::new(5, 0)..Point::new(7, 0),
11726                    primary: None,
11727                },
11728                ExcerptRange {
11729                    context: Point::new(9, 0)..Point::new(10, 4),
11730                    primary: None,
11731                },
11732            ],
11733            cx,
11734        );
11735        multibuffer.push_excerpts(
11736            buffer_2.clone(),
11737            [
11738                ExcerptRange {
11739                    context: Point::new(0, 0)..Point::new(3, 0),
11740                    primary: None,
11741                },
11742                ExcerptRange {
11743                    context: Point::new(5, 0)..Point::new(7, 0),
11744                    primary: None,
11745                },
11746                ExcerptRange {
11747                    context: Point::new(9, 0)..Point::new(10, 4),
11748                    primary: None,
11749                },
11750            ],
11751            cx,
11752        );
11753        multibuffer.push_excerpts(
11754            buffer_3.clone(),
11755            [
11756                ExcerptRange {
11757                    context: Point::new(0, 0)..Point::new(3, 0),
11758                    primary: None,
11759                },
11760                ExcerptRange {
11761                    context: Point::new(5, 0)..Point::new(7, 0),
11762                    primary: None,
11763                },
11764                ExcerptRange {
11765                    context: Point::new(9, 0)..Point::new(10, 4),
11766                    primary: None,
11767                },
11768            ],
11769            cx,
11770        );
11771        multibuffer
11772    });
11773
11774    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11775    editor.update(cx, |editor, cx| {
11776        for (buffer, diff_base) in [
11777            (buffer_1.clone(), base_text_1),
11778            (buffer_2.clone(), base_text_2),
11779            (buffer_3.clone(), base_text_3),
11780        ] {
11781            let change_set = cx.new_model(|cx| {
11782                BufferChangeSet::new_with_base_text(
11783                    diff_base.to_string(),
11784                    buffer.read(cx).text_snapshot(),
11785                    cx,
11786                )
11787            });
11788            editor.diff_map.add_change_set(change_set, cx)
11789        }
11790    });
11791    cx.executor().run_until_parked();
11792
11793    editor.update(cx, |editor, cx| {
11794        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}");
11795        editor.select_all(&SelectAll, cx);
11796        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11797    });
11798    cx.executor().run_until_parked();
11799
11800    // When all ranges are selected, all buffer hunks are reverted.
11801    editor.update(cx, |editor, cx| {
11802        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");
11803    });
11804    buffer_1.update(cx, |buffer, _| {
11805        assert_eq!(buffer.text(), base_text_1);
11806    });
11807    buffer_2.update(cx, |buffer, _| {
11808        assert_eq!(buffer.text(), base_text_2);
11809    });
11810    buffer_3.update(cx, |buffer, _| {
11811        assert_eq!(buffer.text(), base_text_3);
11812    });
11813
11814    editor.update(cx, |editor, cx| {
11815        editor.undo(&Default::default(), cx);
11816    });
11817
11818    editor.update(cx, |editor, cx| {
11819        editor.change_selections(None, cx, |s| {
11820            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11821        });
11822        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11823    });
11824
11825    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11826    // but not affect buffer_2 and its related excerpts.
11827    editor.update(cx, |editor, cx| {
11828        assert_eq!(
11829            editor.text(cx),
11830            "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}"
11831        );
11832    });
11833    buffer_1.update(cx, |buffer, _| {
11834        assert_eq!(buffer.text(), base_text_1);
11835    });
11836    buffer_2.update(cx, |buffer, _| {
11837        assert_eq!(
11838            buffer.text(),
11839            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11840        );
11841    });
11842    buffer_3.update(cx, |buffer, _| {
11843        assert_eq!(
11844            buffer.text(),
11845            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11846        );
11847    });
11848
11849    fn edit_first_char_of_every_line(text: &str) -> String {
11850        text.split('\n')
11851            .map(|line| format!("X{}", &line[1..]))
11852            .collect::<Vec<_>>()
11853            .join("\n")
11854    }
11855}
11856
11857#[gpui::test]
11858async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11859    init_test(cx, |_| {});
11860
11861    let cols = 4;
11862    let rows = 10;
11863    let sample_text_1 = sample_text(rows, cols, 'a');
11864    assert_eq!(
11865        sample_text_1,
11866        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11867    );
11868    let sample_text_2 = sample_text(rows, cols, 'l');
11869    assert_eq!(
11870        sample_text_2,
11871        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11872    );
11873    let sample_text_3 = sample_text(rows, cols, 'v');
11874    assert_eq!(
11875        sample_text_3,
11876        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11877    );
11878
11879    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11880    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11881    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11882
11883    let multi_buffer = cx.new_model(|cx| {
11884        let mut multibuffer = MultiBuffer::new(ReadWrite);
11885        multibuffer.push_excerpts(
11886            buffer_1.clone(),
11887            [
11888                ExcerptRange {
11889                    context: Point::new(0, 0)..Point::new(3, 0),
11890                    primary: None,
11891                },
11892                ExcerptRange {
11893                    context: Point::new(5, 0)..Point::new(7, 0),
11894                    primary: None,
11895                },
11896                ExcerptRange {
11897                    context: Point::new(9, 0)..Point::new(10, 4),
11898                    primary: None,
11899                },
11900            ],
11901            cx,
11902        );
11903        multibuffer.push_excerpts(
11904            buffer_2.clone(),
11905            [
11906                ExcerptRange {
11907                    context: Point::new(0, 0)..Point::new(3, 0),
11908                    primary: None,
11909                },
11910                ExcerptRange {
11911                    context: Point::new(5, 0)..Point::new(7, 0),
11912                    primary: None,
11913                },
11914                ExcerptRange {
11915                    context: Point::new(9, 0)..Point::new(10, 4),
11916                    primary: None,
11917                },
11918            ],
11919            cx,
11920        );
11921        multibuffer.push_excerpts(
11922            buffer_3.clone(),
11923            [
11924                ExcerptRange {
11925                    context: Point::new(0, 0)..Point::new(3, 0),
11926                    primary: None,
11927                },
11928                ExcerptRange {
11929                    context: Point::new(5, 0)..Point::new(7, 0),
11930                    primary: None,
11931                },
11932                ExcerptRange {
11933                    context: Point::new(9, 0)..Point::new(10, 4),
11934                    primary: None,
11935                },
11936            ],
11937            cx,
11938        );
11939        multibuffer
11940    });
11941
11942    let fs = FakeFs::new(cx.executor());
11943    fs.insert_tree(
11944        "/a",
11945        json!({
11946            "main.rs": sample_text_1,
11947            "other.rs": sample_text_2,
11948            "lib.rs": sample_text_3,
11949        }),
11950    )
11951    .await;
11952    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11953    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11954    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11955    let multi_buffer_editor = cx.new_view(|cx| {
11956        Editor::new(
11957            EditorMode::Full,
11958            multi_buffer,
11959            Some(project.clone()),
11960            true,
11961            cx,
11962        )
11963    });
11964    let multibuffer_item_id = workspace
11965        .update(cx, |workspace, cx| {
11966            assert!(
11967                workspace.active_item(cx).is_none(),
11968                "active item should be None before the first item is added"
11969            );
11970            workspace.add_item_to_active_pane(
11971                Box::new(multi_buffer_editor.clone()),
11972                None,
11973                true,
11974                cx,
11975            );
11976            let active_item = workspace
11977                .active_item(cx)
11978                .expect("should have an active item after adding the multi buffer");
11979            assert!(
11980                !active_item.is_singleton(cx),
11981                "A multi buffer was expected to active after adding"
11982            );
11983            active_item.item_id()
11984        })
11985        .unwrap();
11986    cx.executor().run_until_parked();
11987
11988    multi_buffer_editor.update(cx, |editor, cx| {
11989        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11990        editor.open_excerpts(&OpenExcerpts, cx);
11991    });
11992    cx.executor().run_until_parked();
11993    let first_item_id = workspace
11994        .update(cx, |workspace, cx| {
11995            let active_item = workspace
11996                .active_item(cx)
11997                .expect("should have an active item after navigating into the 1st buffer");
11998            let first_item_id = active_item.item_id();
11999            assert_ne!(
12000                first_item_id, multibuffer_item_id,
12001                "Should navigate into the 1st buffer and activate it"
12002            );
12003            assert!(
12004                active_item.is_singleton(cx),
12005                "New active item should be a singleton buffer"
12006            );
12007            assert_eq!(
12008                active_item
12009                    .act_as::<Editor>(cx)
12010                    .expect("should have navigated into an editor for the 1st buffer")
12011                    .read(cx)
12012                    .text(cx),
12013                sample_text_1
12014            );
12015
12016            workspace
12017                .go_back(workspace.active_pane().downgrade(), cx)
12018                .detach_and_log_err(cx);
12019
12020            first_item_id
12021        })
12022        .unwrap();
12023    cx.executor().run_until_parked();
12024    workspace
12025        .update(cx, |workspace, cx| {
12026            let active_item = workspace
12027                .active_item(cx)
12028                .expect("should have an active item after navigating back");
12029            assert_eq!(
12030                active_item.item_id(),
12031                multibuffer_item_id,
12032                "Should navigate back to the multi buffer"
12033            );
12034            assert!(!active_item.is_singleton(cx));
12035        })
12036        .unwrap();
12037
12038    multi_buffer_editor.update(cx, |editor, cx| {
12039        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12040            s.select_ranges(Some(39..40))
12041        });
12042        editor.open_excerpts(&OpenExcerpts, cx);
12043    });
12044    cx.executor().run_until_parked();
12045    let second_item_id = workspace
12046        .update(cx, |workspace, cx| {
12047            let active_item = workspace
12048                .active_item(cx)
12049                .expect("should have an active item after navigating into the 2nd buffer");
12050            let second_item_id = active_item.item_id();
12051            assert_ne!(
12052                second_item_id, multibuffer_item_id,
12053                "Should navigate away from the multibuffer"
12054            );
12055            assert_ne!(
12056                second_item_id, first_item_id,
12057                "Should navigate into the 2nd buffer and activate it"
12058            );
12059            assert!(
12060                active_item.is_singleton(cx),
12061                "New active item should be a singleton buffer"
12062            );
12063            assert_eq!(
12064                active_item
12065                    .act_as::<Editor>(cx)
12066                    .expect("should have navigated into an editor")
12067                    .read(cx)
12068                    .text(cx),
12069                sample_text_2
12070            );
12071
12072            workspace
12073                .go_back(workspace.active_pane().downgrade(), cx)
12074                .detach_and_log_err(cx);
12075
12076            second_item_id
12077        })
12078        .unwrap();
12079    cx.executor().run_until_parked();
12080    workspace
12081        .update(cx, |workspace, cx| {
12082            let active_item = workspace
12083                .active_item(cx)
12084                .expect("should have an active item after navigating back from the 2nd buffer");
12085            assert_eq!(
12086                active_item.item_id(),
12087                multibuffer_item_id,
12088                "Should navigate back from the 2nd buffer to the multi buffer"
12089            );
12090            assert!(!active_item.is_singleton(cx));
12091        })
12092        .unwrap();
12093
12094    multi_buffer_editor.update(cx, |editor, cx| {
12095        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12096            s.select_ranges(Some(70..70))
12097        });
12098        editor.open_excerpts(&OpenExcerpts, cx);
12099    });
12100    cx.executor().run_until_parked();
12101    workspace
12102        .update(cx, |workspace, cx| {
12103            let active_item = workspace
12104                .active_item(cx)
12105                .expect("should have an active item after navigating into the 3rd buffer");
12106            let third_item_id = active_item.item_id();
12107            assert_ne!(
12108                third_item_id, multibuffer_item_id,
12109                "Should navigate into the 3rd buffer and activate it"
12110            );
12111            assert_ne!(third_item_id, first_item_id);
12112            assert_ne!(third_item_id, second_item_id);
12113            assert!(
12114                active_item.is_singleton(cx),
12115                "New active item should be a singleton buffer"
12116            );
12117            assert_eq!(
12118                active_item
12119                    .act_as::<Editor>(cx)
12120                    .expect("should have navigated into an editor")
12121                    .read(cx)
12122                    .text(cx),
12123                sample_text_3
12124            );
12125
12126            workspace
12127                .go_back(workspace.active_pane().downgrade(), cx)
12128                .detach_and_log_err(cx);
12129        })
12130        .unwrap();
12131    cx.executor().run_until_parked();
12132    workspace
12133        .update(cx, |workspace, cx| {
12134            let active_item = workspace
12135                .active_item(cx)
12136                .expect("should have an active item after navigating back from the 3rd buffer");
12137            assert_eq!(
12138                active_item.item_id(),
12139                multibuffer_item_id,
12140                "Should navigate back from the 3rd buffer to the multi buffer"
12141            );
12142            assert!(!active_item.is_singleton(cx));
12143        })
12144        .unwrap();
12145}
12146
12147#[gpui::test]
12148async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12149    init_test(cx, |_| {});
12150
12151    let mut cx = EditorTestContext::new(cx).await;
12152
12153    let diff_base = r#"
12154        use some::mod;
12155
12156        const A: u32 = 42;
12157
12158        fn main() {
12159            println!("hello");
12160
12161            println!("world");
12162        }
12163        "#
12164    .unindent();
12165
12166    cx.set_state(
12167        &r#"
12168        use some::modified;
12169
12170        ˇ
12171        fn main() {
12172            println!("hello there");
12173
12174            println!("around the");
12175            println!("world");
12176        }
12177        "#
12178        .unindent(),
12179    );
12180
12181    cx.set_diff_base(&diff_base);
12182    executor.run_until_parked();
12183
12184    cx.update_editor(|editor, cx| {
12185        editor.go_to_next_hunk(&GoToHunk, cx);
12186        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12187    });
12188    executor.run_until_parked();
12189    cx.assert_state_with_diff(
12190        r#"
12191          use some::modified;
12192
12193
12194          fn main() {
12195        -     println!("hello");
12196        + ˇ    println!("hello there");
12197
12198              println!("around the");
12199              println!("world");
12200          }
12201        "#
12202        .unindent(),
12203    );
12204
12205    cx.update_editor(|editor, cx| {
12206        for _ in 0..3 {
12207            editor.go_to_next_hunk(&GoToHunk, cx);
12208            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12209        }
12210    });
12211    executor.run_until_parked();
12212    cx.assert_state_with_diff(
12213        r#"
12214        - use some::mod;
12215        + use some::modified;
12216
12217        - const A: u32 = 42;
12218          ˇ
12219          fn main() {
12220        -     println!("hello");
12221        +     println!("hello there");
12222
12223        +     println!("around the");
12224              println!("world");
12225          }
12226        "#
12227        .unindent(),
12228    );
12229
12230    cx.update_editor(|editor, cx| {
12231        editor.cancel(&Cancel, cx);
12232    });
12233
12234    cx.assert_state_with_diff(
12235        r#"
12236          use some::modified;
12237
12238          ˇ
12239          fn main() {
12240              println!("hello there");
12241
12242              println!("around the");
12243              println!("world");
12244          }
12245        "#
12246        .unindent(),
12247    );
12248}
12249
12250#[gpui::test]
12251async fn test_diff_base_change_with_expanded_diff_hunks(
12252    executor: BackgroundExecutor,
12253    cx: &mut gpui::TestAppContext,
12254) {
12255    init_test(cx, |_| {});
12256
12257    let mut cx = EditorTestContext::new(cx).await;
12258
12259    let diff_base = r#"
12260        use some::mod1;
12261        use some::mod2;
12262
12263        const A: u32 = 42;
12264        const B: u32 = 42;
12265        const C: u32 = 42;
12266
12267        fn main() {
12268            println!("hello");
12269
12270            println!("world");
12271        }
12272        "#
12273    .unindent();
12274
12275    cx.set_state(
12276        &r#"
12277        use some::mod2;
12278
12279        const A: u32 = 42;
12280        const C: u32 = 42;
12281
12282        fn main(ˇ) {
12283            //println!("hello");
12284
12285            println!("world");
12286            //
12287            //
12288        }
12289        "#
12290        .unindent(),
12291    );
12292
12293    cx.set_diff_base(&diff_base);
12294    executor.run_until_parked();
12295
12296    cx.update_editor(|editor, cx| {
12297        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12298    });
12299    executor.run_until_parked();
12300    cx.assert_state_with_diff(
12301        r#"
12302        - use some::mod1;
12303          use some::mod2;
12304
12305          const A: u32 = 42;
12306        - const B: u32 = 42;
12307          const C: u32 = 42;
12308
12309          fn main(ˇ) {
12310        -     println!("hello");
12311        +     //println!("hello");
12312
12313              println!("world");
12314        +     //
12315        +     //
12316          }
12317        "#
12318        .unindent(),
12319    );
12320
12321    cx.set_diff_base("new diff base!");
12322    executor.run_until_parked();
12323    cx.assert_state_with_diff(
12324        r#"
12325          use some::mod2;
12326
12327          const A: u32 = 42;
12328          const C: u32 = 42;
12329
12330          fn main(ˇ) {
12331              //println!("hello");
12332
12333              println!("world");
12334              //
12335              //
12336          }
12337        "#
12338        .unindent(),
12339    );
12340
12341    cx.update_editor(|editor, cx| {
12342        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12343    });
12344    executor.run_until_parked();
12345    cx.assert_state_with_diff(
12346        r#"
12347        - new diff base!
12348        + use some::mod2;
12349        +
12350        + const A: u32 = 42;
12351        + const C: u32 = 42;
12352        +
12353        + fn main(ˇ) {
12354        +     //println!("hello");
12355        +
12356        +     println!("world");
12357        +     //
12358        +     //
12359        + }
12360        "#
12361        .unindent(),
12362    );
12363}
12364
12365#[gpui::test]
12366async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12367    init_test(cx, |_| {});
12368
12369    let mut cx = EditorTestContext::new(cx).await;
12370
12371    let diff_base = r#"
12372        use some::mod1;
12373        use some::mod2;
12374
12375        const A: u32 = 42;
12376        const B: u32 = 42;
12377        const C: u32 = 42;
12378
12379        fn main() {
12380            println!("hello");
12381
12382            println!("world");
12383        }
12384
12385        fn another() {
12386            println!("another");
12387        }
12388
12389        fn another2() {
12390            println!("another2");
12391        }
12392        "#
12393    .unindent();
12394
12395    cx.set_state(
12396        &r#"
12397        «use some::mod2;
12398
12399        const A: u32 = 42;
12400        const C: u32 = 42;
12401
12402        fn main() {
12403            //println!("hello");
12404
12405            println!("world");
12406            //
12407            //ˇ»
12408        }
12409
12410        fn another() {
12411            println!("another");
12412            println!("another");
12413        }
12414
12415            println!("another2");
12416        }
12417        "#
12418        .unindent(),
12419    );
12420
12421    cx.set_diff_base(&diff_base);
12422    executor.run_until_parked();
12423
12424    cx.update_editor(|editor, cx| {
12425        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12426    });
12427    executor.run_until_parked();
12428
12429    cx.assert_state_with_diff(
12430        r#"
12431        - use some::mod1;
12432          «use some::mod2;
12433
12434          const A: u32 = 42;
12435        - const B: u32 = 42;
12436          const C: u32 = 42;
12437
12438          fn main() {
12439        -     println!("hello");
12440        +     //println!("hello");
12441
12442              println!("world");
12443        +     //
12444        +     //ˇ»
12445          }
12446
12447          fn another() {
12448              println!("another");
12449        +     println!("another");
12450          }
12451
12452        - fn another2() {
12453              println!("another2");
12454          }
12455        "#
12456        .unindent(),
12457    );
12458
12459    // Fold across some of the diff hunks. They should no longer appear expanded.
12460    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12461    cx.executor().run_until_parked();
12462
12463    // Hunks are not shown if their position is within a fold
12464    cx.assert_state_with_diff(
12465        r#"
12466          «use some::mod2;
12467
12468          const A: u32 = 42;
12469          const C: u32 = 42;
12470
12471          fn main() {
12472              //println!("hello");
12473
12474              println!("world");
12475              //
12476              //ˇ»
12477          }
12478
12479          fn another() {
12480              println!("another");
12481        +     println!("another");
12482          }
12483
12484        - fn another2() {
12485              println!("another2");
12486          }
12487        "#
12488        .unindent(),
12489    );
12490
12491    cx.update_editor(|editor, cx| {
12492        editor.select_all(&SelectAll, cx);
12493        editor.unfold_lines(&UnfoldLines, cx);
12494    });
12495    cx.executor().run_until_parked();
12496
12497    // The deletions reappear when unfolding.
12498    cx.assert_state_with_diff(
12499        r#"
12500        - use some::mod1;
12501          «use some::mod2;
12502
12503          const A: u32 = 42;
12504        - const B: u32 = 42;
12505          const C: u32 = 42;
12506
12507          fn main() {
12508        -     println!("hello");
12509        +     //println!("hello");
12510
12511              println!("world");
12512        +     //
12513        +     //
12514          }
12515
12516          fn another() {
12517              println!("another");
12518        +     println!("another");
12519          }
12520
12521        - fn another2() {
12522              println!("another2");
12523          }
12524          ˇ»"#
12525        .unindent(),
12526    );
12527}
12528
12529#[gpui::test]
12530async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12531    init_test(cx, |_| {});
12532
12533    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12534    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12535    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12536    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12537    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12538    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12539
12540    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12541    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12542    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12543
12544    let multi_buffer = cx.new_model(|cx| {
12545        let mut multibuffer = MultiBuffer::new(ReadWrite);
12546        multibuffer.push_excerpts(
12547            buffer_1.clone(),
12548            [
12549                ExcerptRange {
12550                    context: Point::new(0, 0)..Point::new(3, 0),
12551                    primary: None,
12552                },
12553                ExcerptRange {
12554                    context: Point::new(5, 0)..Point::new(7, 0),
12555                    primary: None,
12556                },
12557                ExcerptRange {
12558                    context: Point::new(9, 0)..Point::new(10, 3),
12559                    primary: None,
12560                },
12561            ],
12562            cx,
12563        );
12564        multibuffer.push_excerpts(
12565            buffer_2.clone(),
12566            [
12567                ExcerptRange {
12568                    context: Point::new(0, 0)..Point::new(3, 0),
12569                    primary: None,
12570                },
12571                ExcerptRange {
12572                    context: Point::new(5, 0)..Point::new(7, 0),
12573                    primary: None,
12574                },
12575                ExcerptRange {
12576                    context: Point::new(9, 0)..Point::new(10, 3),
12577                    primary: None,
12578                },
12579            ],
12580            cx,
12581        );
12582        multibuffer.push_excerpts(
12583            buffer_3.clone(),
12584            [
12585                ExcerptRange {
12586                    context: Point::new(0, 0)..Point::new(3, 0),
12587                    primary: None,
12588                },
12589                ExcerptRange {
12590                    context: Point::new(5, 0)..Point::new(7, 0),
12591                    primary: None,
12592                },
12593                ExcerptRange {
12594                    context: Point::new(9, 0)..Point::new(10, 3),
12595                    primary: None,
12596                },
12597            ],
12598            cx,
12599        );
12600        multibuffer
12601    });
12602
12603    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12604    editor
12605        .update(cx, |editor, cx| {
12606            for (buffer, diff_base) in [
12607                (buffer_1.clone(), file_1_old),
12608                (buffer_2.clone(), file_2_old),
12609                (buffer_3.clone(), file_3_old),
12610            ] {
12611                let change_set = cx.new_model(|cx| {
12612                    BufferChangeSet::new_with_base_text(
12613                        diff_base.to_string(),
12614                        buffer.read(cx).text_snapshot(),
12615                        cx,
12616                    )
12617                });
12618                editor.diff_map.add_change_set(change_set, cx)
12619            }
12620        })
12621        .unwrap();
12622
12623    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12624    cx.run_until_parked();
12625
12626    cx.assert_editor_state(
12627        &"
12628            ˇaaa
12629            ccc
12630            ddd
12631
12632            ggg
12633            hhh
12634
12635
12636            lll
12637            mmm
12638            NNN
12639
12640            qqq
12641            rrr
12642
12643            uuu
12644            111
12645            222
12646            333
12647
12648            666
12649            777
12650
12651            000
12652            !!!"
12653        .unindent(),
12654    );
12655
12656    cx.update_editor(|editor, cx| {
12657        editor.select_all(&SelectAll, cx);
12658        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12659    });
12660    cx.executor().run_until_parked();
12661
12662    cx.assert_state_with_diff(
12663        "
12664            «aaa
12665          - bbb
12666            ccc
12667            ddd
12668
12669            ggg
12670            hhh
12671
12672
12673            lll
12674            mmm
12675          - nnn
12676          + NNN
12677
12678            qqq
12679            rrr
12680
12681            uuu
12682            111
12683            222
12684            333
12685
12686          + 666
12687            777
12688
12689            000
12690            !!!ˇ»"
12691            .unindent(),
12692    );
12693}
12694
12695#[gpui::test]
12696async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12697    init_test(cx, |_| {});
12698
12699    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12700    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12701
12702    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12703    let multi_buffer = cx.new_model(|cx| {
12704        let mut multibuffer = MultiBuffer::new(ReadWrite);
12705        multibuffer.push_excerpts(
12706            buffer.clone(),
12707            [
12708                ExcerptRange {
12709                    context: Point::new(0, 0)..Point::new(2, 0),
12710                    primary: None,
12711                },
12712                ExcerptRange {
12713                    context: Point::new(5, 0)..Point::new(7, 0),
12714                    primary: None,
12715                },
12716            ],
12717            cx,
12718        );
12719        multibuffer
12720    });
12721
12722    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12723    editor
12724        .update(cx, |editor, cx| {
12725            let buffer = buffer.read(cx).text_snapshot();
12726            let change_set = cx
12727                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12728            editor.diff_map.add_change_set(change_set, cx)
12729        })
12730        .unwrap();
12731
12732    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12733    cx.run_until_parked();
12734
12735    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12736    cx.executor().run_until_parked();
12737
12738    cx.assert_state_with_diff(
12739        "
12740            ˇaaa
12741          - bbb
12742          + BBB
12743
12744          - ddd
12745          - eee
12746          + EEE
12747            fff
12748        "
12749        .unindent(),
12750    );
12751}
12752
12753#[gpui::test]
12754async fn test_edits_around_expanded_insertion_hunks(
12755    executor: BackgroundExecutor,
12756    cx: &mut gpui::TestAppContext,
12757) {
12758    init_test(cx, |_| {});
12759
12760    let mut cx = EditorTestContext::new(cx).await;
12761
12762    let diff_base = r#"
12763        use some::mod1;
12764        use some::mod2;
12765
12766        const A: u32 = 42;
12767
12768        fn main() {
12769            println!("hello");
12770
12771            println!("world");
12772        }
12773        "#
12774    .unindent();
12775    executor.run_until_parked();
12776    cx.set_state(
12777        &r#"
12778        use some::mod1;
12779        use some::mod2;
12780
12781        const A: u32 = 42;
12782        const B: u32 = 42;
12783        const C: u32 = 42;
12784        ˇ
12785
12786        fn main() {
12787            println!("hello");
12788
12789            println!("world");
12790        }
12791        "#
12792        .unindent(),
12793    );
12794
12795    cx.set_diff_base(&diff_base);
12796    executor.run_until_parked();
12797
12798    cx.update_editor(|editor, cx| {
12799        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12800    });
12801    executor.run_until_parked();
12802
12803    cx.assert_state_with_diff(
12804        r#"
12805        use some::mod1;
12806        use some::mod2;
12807
12808        const A: u32 = 42;
12809      + const B: u32 = 42;
12810      + const C: u32 = 42;
12811      + ˇ
12812
12813        fn main() {
12814            println!("hello");
12815
12816            println!("world");
12817        }
12818        "#
12819        .unindent(),
12820    );
12821
12822    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12823    executor.run_until_parked();
12824
12825    cx.assert_state_with_diff(
12826        r#"
12827        use some::mod1;
12828        use some::mod2;
12829
12830        const A: u32 = 42;
12831      + const B: u32 = 42;
12832      + const C: u32 = 42;
12833      + const D: u32 = 42;
12834      + ˇ
12835
12836        fn main() {
12837            println!("hello");
12838
12839            println!("world");
12840        }
12841        "#
12842        .unindent(),
12843    );
12844
12845    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12846    executor.run_until_parked();
12847
12848    cx.assert_state_with_diff(
12849        r#"
12850        use some::mod1;
12851        use some::mod2;
12852
12853        const A: u32 = 42;
12854      + const B: u32 = 42;
12855      + const C: u32 = 42;
12856      + const D: u32 = 42;
12857      + const E: u32 = 42;
12858      + ˇ
12859
12860        fn main() {
12861            println!("hello");
12862
12863            println!("world");
12864        }
12865        "#
12866        .unindent(),
12867    );
12868
12869    cx.update_editor(|editor, cx| {
12870        editor.delete_line(&DeleteLine, cx);
12871    });
12872    executor.run_until_parked();
12873
12874    cx.assert_state_with_diff(
12875        r#"
12876        use some::mod1;
12877        use some::mod2;
12878
12879        const A: u32 = 42;
12880      + const B: u32 = 42;
12881      + const C: u32 = 42;
12882      + const D: u32 = 42;
12883      + const E: u32 = 42;
12884        ˇ
12885        fn main() {
12886            println!("hello");
12887
12888            println!("world");
12889        }
12890        "#
12891        .unindent(),
12892    );
12893
12894    cx.update_editor(|editor, cx| {
12895        editor.move_up(&MoveUp, cx);
12896        editor.delete_line(&DeleteLine, cx);
12897        editor.move_up(&MoveUp, cx);
12898        editor.delete_line(&DeleteLine, cx);
12899        editor.move_up(&MoveUp, cx);
12900        editor.delete_line(&DeleteLine, cx);
12901    });
12902    executor.run_until_parked();
12903    cx.assert_state_with_diff(
12904        r#"
12905        use some::mod1;
12906        use some::mod2;
12907
12908        const A: u32 = 42;
12909      + const B: u32 = 42;
12910        ˇ
12911        fn main() {
12912            println!("hello");
12913
12914            println!("world");
12915        }
12916        "#
12917        .unindent(),
12918    );
12919
12920    cx.update_editor(|editor, cx| {
12921        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12922        editor.delete_line(&DeleteLine, cx);
12923    });
12924    executor.run_until_parked();
12925    cx.assert_state_with_diff(
12926        r#"
12927        use some::mod1;
12928      - use some::mod2;
12929      -
12930      - const A: u32 = 42;
12931        ˇ
12932        fn main() {
12933            println!("hello");
12934
12935            println!("world");
12936        }
12937        "#
12938        .unindent(),
12939    );
12940}
12941
12942#[gpui::test]
12943async fn test_edits_around_expanded_deletion_hunks(
12944    executor: BackgroundExecutor,
12945    cx: &mut gpui::TestAppContext,
12946) {
12947    init_test(cx, |_| {});
12948
12949    let mut cx = EditorTestContext::new(cx).await;
12950
12951    let diff_base = r#"
12952        use some::mod1;
12953        use some::mod2;
12954
12955        const A: u32 = 42;
12956        const B: u32 = 42;
12957        const C: u32 = 42;
12958
12959
12960        fn main() {
12961            println!("hello");
12962
12963            println!("world");
12964        }
12965    "#
12966    .unindent();
12967    executor.run_until_parked();
12968    cx.set_state(
12969        &r#"
12970        use some::mod1;
12971        use some::mod2;
12972
12973        ˇconst B: u32 = 42;
12974        const C: u32 = 42;
12975
12976
12977        fn main() {
12978            println!("hello");
12979
12980            println!("world");
12981        }
12982        "#
12983        .unindent(),
12984    );
12985
12986    cx.set_diff_base(&diff_base);
12987    executor.run_until_parked();
12988
12989    cx.update_editor(|editor, cx| {
12990        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12991    });
12992    executor.run_until_parked();
12993
12994    cx.assert_state_with_diff(
12995        r#"
12996        use some::mod1;
12997        use some::mod2;
12998
12999      - const A: u32 = 42;
13000        ˇconst B: u32 = 42;
13001        const C: u32 = 42;
13002
13003
13004        fn main() {
13005            println!("hello");
13006
13007            println!("world");
13008        }
13009        "#
13010        .unindent(),
13011    );
13012
13013    cx.update_editor(|editor, cx| {
13014        editor.delete_line(&DeleteLine, cx);
13015    });
13016    executor.run_until_parked();
13017    cx.assert_state_with_diff(
13018        r#"
13019        use some::mod1;
13020        use some::mod2;
13021
13022      - const A: u32 = 42;
13023      - const B: u32 = 42;
13024        ˇconst C: u32 = 42;
13025
13026
13027        fn main() {
13028            println!("hello");
13029
13030            println!("world");
13031        }
13032        "#
13033        .unindent(),
13034    );
13035
13036    cx.update_editor(|editor, cx| {
13037        editor.delete_line(&DeleteLine, cx);
13038    });
13039    executor.run_until_parked();
13040    cx.assert_state_with_diff(
13041        r#"
13042        use some::mod1;
13043        use some::mod2;
13044
13045      - const A: u32 = 42;
13046      - const B: u32 = 42;
13047      - const C: u32 = 42;
13048        ˇ
13049
13050        fn main() {
13051            println!("hello");
13052
13053            println!("world");
13054        }
13055        "#
13056        .unindent(),
13057    );
13058
13059    cx.update_editor(|editor, cx| {
13060        editor.handle_input("replacement", cx);
13061    });
13062    executor.run_until_parked();
13063    cx.assert_state_with_diff(
13064        r#"
13065        use some::mod1;
13066        use some::mod2;
13067
13068      - const A: u32 = 42;
13069      - const B: u32 = 42;
13070      - const C: u32 = 42;
13071      -
13072      + replacementˇ
13073
13074        fn main() {
13075            println!("hello");
13076
13077            println!("world");
13078        }
13079        "#
13080        .unindent(),
13081    );
13082}
13083
13084#[gpui::test]
13085async fn test_edit_after_expanded_modification_hunk(
13086    executor: BackgroundExecutor,
13087    cx: &mut gpui::TestAppContext,
13088) {
13089    init_test(cx, |_| {});
13090
13091    let mut cx = EditorTestContext::new(cx).await;
13092
13093    let diff_base = r#"
13094        use some::mod1;
13095        use some::mod2;
13096
13097        const A: u32 = 42;
13098        const B: u32 = 42;
13099        const C: u32 = 42;
13100        const D: u32 = 42;
13101
13102
13103        fn main() {
13104            println!("hello");
13105
13106            println!("world");
13107        }"#
13108    .unindent();
13109
13110    cx.set_state(
13111        &r#"
13112        use some::mod1;
13113        use some::mod2;
13114
13115        const A: u32 = 42;
13116        const B: u32 = 42;
13117        const C: u32 = 43ˇ
13118        const D: u32 = 42;
13119
13120
13121        fn main() {
13122            println!("hello");
13123
13124            println!("world");
13125        }"#
13126        .unindent(),
13127    );
13128
13129    cx.set_diff_base(&diff_base);
13130    executor.run_until_parked();
13131    cx.update_editor(|editor, cx| {
13132        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13133    });
13134    executor.run_until_parked();
13135
13136    cx.assert_state_with_diff(
13137        r#"
13138        use some::mod1;
13139        use some::mod2;
13140
13141        const A: u32 = 42;
13142        const B: u32 = 42;
13143      - const C: u32 = 42;
13144      + const C: u32 = 43ˇ
13145        const D: u32 = 42;
13146
13147
13148        fn main() {
13149            println!("hello");
13150
13151            println!("world");
13152        }"#
13153        .unindent(),
13154    );
13155
13156    cx.update_editor(|editor, cx| {
13157        editor.handle_input("\nnew_line\n", cx);
13158    });
13159    executor.run_until_parked();
13160
13161    cx.assert_state_with_diff(
13162        r#"
13163        use some::mod1;
13164        use some::mod2;
13165
13166        const A: u32 = 42;
13167        const B: u32 = 42;
13168      - const C: u32 = 42;
13169      + const C: u32 = 43
13170      + new_line
13171      + ˇ
13172        const D: u32 = 42;
13173
13174
13175        fn main() {
13176            println!("hello");
13177
13178            println!("world");
13179        }"#
13180        .unindent(),
13181    );
13182}
13183
13184async fn setup_indent_guides_editor(
13185    text: &str,
13186    cx: &mut gpui::TestAppContext,
13187) -> (BufferId, EditorTestContext) {
13188    init_test(cx, |_| {});
13189
13190    let mut cx = EditorTestContext::new(cx).await;
13191
13192    let buffer_id = cx.update_editor(|editor, cx| {
13193        editor.set_text(text, cx);
13194        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13195
13196        buffer_ids[0]
13197    });
13198
13199    (buffer_id, cx)
13200}
13201
13202fn assert_indent_guides(
13203    range: Range<u32>,
13204    expected: Vec<IndentGuide>,
13205    active_indices: Option<Vec<usize>>,
13206    cx: &mut EditorTestContext,
13207) {
13208    let indent_guides = cx.update_editor(|editor, cx| {
13209        let snapshot = editor.snapshot(cx).display_snapshot;
13210        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13211            editor,
13212            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13213            true,
13214            &snapshot,
13215            cx,
13216        );
13217
13218        indent_guides.sort_by(|a, b| {
13219            a.depth.cmp(&b.depth).then(
13220                a.start_row
13221                    .cmp(&b.start_row)
13222                    .then(a.end_row.cmp(&b.end_row)),
13223            )
13224        });
13225        indent_guides
13226    });
13227
13228    if let Some(expected) = active_indices {
13229        let active_indices = cx.update_editor(|editor, cx| {
13230            let snapshot = editor.snapshot(cx).display_snapshot;
13231            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13232        });
13233
13234        assert_eq!(
13235            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13236            expected,
13237            "Active indent guide indices do not match"
13238        );
13239    }
13240
13241    let expected: Vec<_> = expected
13242        .into_iter()
13243        .map(|guide| MultiBufferIndentGuide {
13244            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13245            buffer: guide,
13246        })
13247        .collect();
13248
13249    assert_eq!(indent_guides, expected, "Indent guides do not match");
13250}
13251
13252fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13253    IndentGuide {
13254        buffer_id,
13255        start_row,
13256        end_row,
13257        depth,
13258        tab_size: 4,
13259        settings: IndentGuideSettings {
13260            enabled: true,
13261            line_width: 1,
13262            active_line_width: 1,
13263            ..Default::default()
13264        },
13265    }
13266}
13267
13268#[gpui::test]
13269async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13270    let (buffer_id, mut cx) = setup_indent_guides_editor(
13271        &"
13272    fn main() {
13273        let a = 1;
13274    }"
13275        .unindent(),
13276        cx,
13277    )
13278    .await;
13279
13280    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13281}
13282
13283#[gpui::test]
13284async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13285    let (buffer_id, mut cx) = setup_indent_guides_editor(
13286        &"
13287    fn main() {
13288        let a = 1;
13289        let b = 2;
13290    }"
13291        .unindent(),
13292        cx,
13293    )
13294    .await;
13295
13296    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13297}
13298
13299#[gpui::test]
13300async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13301    let (buffer_id, mut cx) = setup_indent_guides_editor(
13302        &"
13303    fn main() {
13304        let a = 1;
13305        if a == 3 {
13306            let b = 2;
13307        } else {
13308            let c = 3;
13309        }
13310    }"
13311        .unindent(),
13312        cx,
13313    )
13314    .await;
13315
13316    assert_indent_guides(
13317        0..8,
13318        vec![
13319            indent_guide(buffer_id, 1, 6, 0),
13320            indent_guide(buffer_id, 3, 3, 1),
13321            indent_guide(buffer_id, 5, 5, 1),
13322        ],
13323        None,
13324        &mut cx,
13325    );
13326}
13327
13328#[gpui::test]
13329async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13330    let (buffer_id, mut cx) = setup_indent_guides_editor(
13331        &"
13332    fn main() {
13333        let a = 1;
13334            let b = 2;
13335        let c = 3;
13336    }"
13337        .unindent(),
13338        cx,
13339    )
13340    .await;
13341
13342    assert_indent_guides(
13343        0..5,
13344        vec![
13345            indent_guide(buffer_id, 1, 3, 0),
13346            indent_guide(buffer_id, 2, 2, 1),
13347        ],
13348        None,
13349        &mut cx,
13350    );
13351}
13352
13353#[gpui::test]
13354async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13355    let (buffer_id, mut cx) = setup_indent_guides_editor(
13356        &"
13357        fn main() {
13358            let a = 1;
13359
13360            let c = 3;
13361        }"
13362        .unindent(),
13363        cx,
13364    )
13365    .await;
13366
13367    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13368}
13369
13370#[gpui::test]
13371async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13372    let (buffer_id, mut cx) = setup_indent_guides_editor(
13373        &"
13374        fn main() {
13375            let a = 1;
13376
13377            let c = 3;
13378
13379            if a == 3 {
13380                let b = 2;
13381            } else {
13382                let c = 3;
13383            }
13384        }"
13385        .unindent(),
13386        cx,
13387    )
13388    .await;
13389
13390    assert_indent_guides(
13391        0..11,
13392        vec![
13393            indent_guide(buffer_id, 1, 9, 0),
13394            indent_guide(buffer_id, 6, 6, 1),
13395            indent_guide(buffer_id, 8, 8, 1),
13396        ],
13397        None,
13398        &mut cx,
13399    );
13400}
13401
13402#[gpui::test]
13403async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13404    let (buffer_id, mut cx) = setup_indent_guides_editor(
13405        &"
13406        fn main() {
13407            let a = 1;
13408
13409            let c = 3;
13410
13411            if a == 3 {
13412                let b = 2;
13413            } else {
13414                let c = 3;
13415            }
13416        }"
13417        .unindent(),
13418        cx,
13419    )
13420    .await;
13421
13422    assert_indent_guides(
13423        1..11,
13424        vec![
13425            indent_guide(buffer_id, 1, 9, 0),
13426            indent_guide(buffer_id, 6, 6, 1),
13427            indent_guide(buffer_id, 8, 8, 1),
13428        ],
13429        None,
13430        &mut cx,
13431    );
13432}
13433
13434#[gpui::test]
13435async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13436    let (buffer_id, mut cx) = setup_indent_guides_editor(
13437        &"
13438        fn main() {
13439            let a = 1;
13440
13441            let c = 3;
13442
13443            if a == 3 {
13444                let b = 2;
13445            } else {
13446                let c = 3;
13447            }
13448        }"
13449        .unindent(),
13450        cx,
13451    )
13452    .await;
13453
13454    assert_indent_guides(
13455        1..10,
13456        vec![
13457            indent_guide(buffer_id, 1, 9, 0),
13458            indent_guide(buffer_id, 6, 6, 1),
13459            indent_guide(buffer_id, 8, 8, 1),
13460        ],
13461        None,
13462        &mut cx,
13463    );
13464}
13465
13466#[gpui::test]
13467async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13468    let (buffer_id, mut cx) = setup_indent_guides_editor(
13469        &"
13470        block1
13471            block2
13472                block3
13473                    block4
13474            block2
13475        block1
13476        block1"
13477            .unindent(),
13478        cx,
13479    )
13480    .await;
13481
13482    assert_indent_guides(
13483        1..10,
13484        vec![
13485            indent_guide(buffer_id, 1, 4, 0),
13486            indent_guide(buffer_id, 2, 3, 1),
13487            indent_guide(buffer_id, 3, 3, 2),
13488        ],
13489        None,
13490        &mut cx,
13491    );
13492}
13493
13494#[gpui::test]
13495async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13496    let (buffer_id, mut cx) = setup_indent_guides_editor(
13497        &"
13498        block1
13499            block2
13500                block3
13501
13502        block1
13503        block1"
13504            .unindent(),
13505        cx,
13506    )
13507    .await;
13508
13509    assert_indent_guides(
13510        0..6,
13511        vec![
13512            indent_guide(buffer_id, 1, 2, 0),
13513            indent_guide(buffer_id, 2, 2, 1),
13514        ],
13515        None,
13516        &mut cx,
13517    );
13518}
13519
13520#[gpui::test]
13521async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13522    let (buffer_id, mut cx) = setup_indent_guides_editor(
13523        &"
13524        block1
13525
13526
13527
13528            block2
13529        "
13530        .unindent(),
13531        cx,
13532    )
13533    .await;
13534
13535    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13536}
13537
13538#[gpui::test]
13539async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13540    let (buffer_id, mut cx) = setup_indent_guides_editor(
13541        &"
13542        def a:
13543        \tb = 3
13544        \tif True:
13545        \t\tc = 4
13546        \t\td = 5
13547        \tprint(b)
13548        "
13549        .unindent(),
13550        cx,
13551    )
13552    .await;
13553
13554    assert_indent_guides(
13555        0..6,
13556        vec![
13557            indent_guide(buffer_id, 1, 6, 0),
13558            indent_guide(buffer_id, 3, 4, 1),
13559        ],
13560        None,
13561        &mut cx,
13562    );
13563}
13564
13565#[gpui::test]
13566async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13567    let (buffer_id, mut cx) = setup_indent_guides_editor(
13568        &"
13569    fn main() {
13570        let a = 1;
13571    }"
13572        .unindent(),
13573        cx,
13574    )
13575    .await;
13576
13577    cx.update_editor(|editor, cx| {
13578        editor.change_selections(None, cx, |s| {
13579            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13580        });
13581    });
13582
13583    assert_indent_guides(
13584        0..3,
13585        vec![indent_guide(buffer_id, 1, 1, 0)],
13586        Some(vec![0]),
13587        &mut cx,
13588    );
13589}
13590
13591#[gpui::test]
13592async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13593    let (buffer_id, mut cx) = setup_indent_guides_editor(
13594        &"
13595    fn main() {
13596        if 1 == 2 {
13597            let a = 1;
13598        }
13599    }"
13600        .unindent(),
13601        cx,
13602    )
13603    .await;
13604
13605    cx.update_editor(|editor, cx| {
13606        editor.change_selections(None, cx, |s| {
13607            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13608        });
13609    });
13610
13611    assert_indent_guides(
13612        0..4,
13613        vec![
13614            indent_guide(buffer_id, 1, 3, 0),
13615            indent_guide(buffer_id, 2, 2, 1),
13616        ],
13617        Some(vec![1]),
13618        &mut cx,
13619    );
13620
13621    cx.update_editor(|editor, cx| {
13622        editor.change_selections(None, cx, |s| {
13623            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13624        });
13625    });
13626
13627    assert_indent_guides(
13628        0..4,
13629        vec![
13630            indent_guide(buffer_id, 1, 3, 0),
13631            indent_guide(buffer_id, 2, 2, 1),
13632        ],
13633        Some(vec![1]),
13634        &mut cx,
13635    );
13636
13637    cx.update_editor(|editor, cx| {
13638        editor.change_selections(None, cx, |s| {
13639            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13640        });
13641    });
13642
13643    assert_indent_guides(
13644        0..4,
13645        vec![
13646            indent_guide(buffer_id, 1, 3, 0),
13647            indent_guide(buffer_id, 2, 2, 1),
13648        ],
13649        Some(vec![0]),
13650        &mut cx,
13651    );
13652}
13653
13654#[gpui::test]
13655async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13656    let (buffer_id, mut cx) = setup_indent_guides_editor(
13657        &"
13658    fn main() {
13659        let a = 1;
13660
13661        let b = 2;
13662    }"
13663        .unindent(),
13664        cx,
13665    )
13666    .await;
13667
13668    cx.update_editor(|editor, cx| {
13669        editor.change_selections(None, cx, |s| {
13670            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13671        });
13672    });
13673
13674    assert_indent_guides(
13675        0..5,
13676        vec![indent_guide(buffer_id, 1, 3, 0)],
13677        Some(vec![0]),
13678        &mut cx,
13679    );
13680}
13681
13682#[gpui::test]
13683async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13684    let (buffer_id, mut cx) = setup_indent_guides_editor(
13685        &"
13686    def m:
13687        a = 1
13688        pass"
13689            .unindent(),
13690        cx,
13691    )
13692    .await;
13693
13694    cx.update_editor(|editor, cx| {
13695        editor.change_selections(None, cx, |s| {
13696            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13697        });
13698    });
13699
13700    assert_indent_guides(
13701        0..3,
13702        vec![indent_guide(buffer_id, 1, 2, 0)],
13703        Some(vec![0]),
13704        &mut cx,
13705    );
13706}
13707
13708#[gpui::test]
13709fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13710    init_test(cx, |_| {});
13711
13712    let editor = cx.add_window(|cx| {
13713        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13714        build_editor(buffer, cx)
13715    });
13716
13717    let render_args = Arc::new(Mutex::new(None));
13718    let snapshot = editor
13719        .update(cx, |editor, cx| {
13720            let snapshot = editor.buffer().read(cx).snapshot(cx);
13721            let range =
13722                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13723
13724            struct RenderArgs {
13725                row: MultiBufferRow,
13726                folded: bool,
13727                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13728            }
13729
13730            let crease = Crease::inline(
13731                range,
13732                FoldPlaceholder::test(),
13733                {
13734                    let toggle_callback = render_args.clone();
13735                    move |row, folded, callback, _cx| {
13736                        *toggle_callback.lock() = Some(RenderArgs {
13737                            row,
13738                            folded,
13739                            callback,
13740                        });
13741                        div()
13742                    }
13743                },
13744                |_row, _folded, _cx| div(),
13745            );
13746
13747            editor.insert_creases(Some(crease), cx);
13748            let snapshot = editor.snapshot(cx);
13749            let _div =
13750                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13751            snapshot
13752        })
13753        .unwrap();
13754
13755    let render_args = render_args.lock().take().unwrap();
13756    assert_eq!(render_args.row, MultiBufferRow(1));
13757    assert!(!render_args.folded);
13758    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13759
13760    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13761        .unwrap();
13762    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13763    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13764
13765    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13766        .unwrap();
13767    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13768    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13769}
13770
13771#[gpui::test]
13772async fn test_input_text(cx: &mut gpui::TestAppContext) {
13773    init_test(cx, |_| {});
13774    let mut cx = EditorTestContext::new(cx).await;
13775
13776    cx.set_state(
13777        &r#"ˇone
13778        two
13779
13780        three
13781        fourˇ
13782        five
13783
13784        siˇx"#
13785            .unindent(),
13786    );
13787
13788    cx.dispatch_action(HandleInput(String::new()));
13789    cx.assert_editor_state(
13790        &r#"ˇone
13791        two
13792
13793        three
13794        fourˇ
13795        five
13796
13797        siˇx"#
13798            .unindent(),
13799    );
13800
13801    cx.dispatch_action(HandleInput("AAAA".to_string()));
13802    cx.assert_editor_state(
13803        &r#"AAAAˇone
13804        two
13805
13806        three
13807        fourAAAAˇ
13808        five
13809
13810        siAAAAˇx"#
13811            .unindent(),
13812    );
13813}
13814
13815#[gpui::test]
13816async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13817    init_test(cx, |_| {});
13818
13819    let mut cx = EditorTestContext::new(cx).await;
13820    cx.set_state(
13821        r#"let foo = 1;
13822let foo = 2;
13823let foo = 3;
13824let fooˇ = 4;
13825let foo = 5;
13826let foo = 6;
13827let foo = 7;
13828let foo = 8;
13829let foo = 9;
13830let foo = 10;
13831let foo = 11;
13832let foo = 12;
13833let foo = 13;
13834let foo = 14;
13835let foo = 15;"#,
13836    );
13837
13838    cx.update_editor(|e, cx| {
13839        assert_eq!(
13840            e.next_scroll_position,
13841            NextScrollCursorCenterTopBottom::Center,
13842            "Default next scroll direction is center",
13843        );
13844
13845        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13846        assert_eq!(
13847            e.next_scroll_position,
13848            NextScrollCursorCenterTopBottom::Top,
13849            "After center, next scroll direction should be top",
13850        );
13851
13852        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13853        assert_eq!(
13854            e.next_scroll_position,
13855            NextScrollCursorCenterTopBottom::Bottom,
13856            "After top, next scroll direction should be bottom",
13857        );
13858
13859        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13860        assert_eq!(
13861            e.next_scroll_position,
13862            NextScrollCursorCenterTopBottom::Center,
13863            "After bottom, scrolling should start over",
13864        );
13865
13866        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13867        assert_eq!(
13868            e.next_scroll_position,
13869            NextScrollCursorCenterTopBottom::Top,
13870            "Scrolling continues if retriggered fast enough"
13871        );
13872    });
13873
13874    cx.executor()
13875        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13876    cx.executor().run_until_parked();
13877    cx.update_editor(|e, _| {
13878        assert_eq!(
13879            e.next_scroll_position,
13880            NextScrollCursorCenterTopBottom::Center,
13881            "If scrolling is not triggered fast enough, it should reset"
13882        );
13883    });
13884}
13885
13886#[gpui::test]
13887async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13888    init_test(cx, |_| {});
13889    let mut cx = EditorLspTestContext::new_rust(
13890        lsp::ServerCapabilities {
13891            definition_provider: Some(lsp::OneOf::Left(true)),
13892            references_provider: Some(lsp::OneOf::Left(true)),
13893            ..lsp::ServerCapabilities::default()
13894        },
13895        cx,
13896    )
13897    .await;
13898
13899    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13900        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13901            move |params, _| async move {
13902                if empty_go_to_definition {
13903                    Ok(None)
13904                } else {
13905                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13906                        uri: params.text_document_position_params.text_document.uri,
13907                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13908                    })))
13909                }
13910            },
13911        );
13912        let references =
13913            cx.lsp
13914                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13915                    Ok(Some(vec![lsp::Location {
13916                        uri: params.text_document_position.text_document.uri,
13917                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13918                    }]))
13919                });
13920        (go_to_definition, references)
13921    };
13922
13923    cx.set_state(
13924        &r#"fn one() {
13925            let mut a = ˇtwo();
13926        }
13927
13928        fn two() {}"#
13929            .unindent(),
13930    );
13931    set_up_lsp_handlers(false, &mut cx);
13932    let navigated = cx
13933        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13934        .await
13935        .expect("Failed to navigate to definition");
13936    assert_eq!(
13937        navigated,
13938        Navigated::Yes,
13939        "Should have navigated to definition from the GetDefinition response"
13940    );
13941    cx.assert_editor_state(
13942        &r#"fn one() {
13943            let mut a = two();
13944        }
13945
13946        fn «twoˇ»() {}"#
13947            .unindent(),
13948    );
13949
13950    let editors = cx.update_workspace(|workspace, cx| {
13951        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13952    });
13953    cx.update_editor(|_, test_editor_cx| {
13954        assert_eq!(
13955            editors.len(),
13956            1,
13957            "Initially, only one, test, editor should be open in the workspace"
13958        );
13959        assert_eq!(
13960            test_editor_cx.view(),
13961            editors.last().expect("Asserted len is 1")
13962        );
13963    });
13964
13965    set_up_lsp_handlers(true, &mut cx);
13966    let navigated = cx
13967        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13968        .await
13969        .expect("Failed to navigate to lookup references");
13970    assert_eq!(
13971        navigated,
13972        Navigated::Yes,
13973        "Should have navigated to references as a fallback after empty GoToDefinition response"
13974    );
13975    // We should not change the selections in the existing file,
13976    // if opening another milti buffer with the references
13977    cx.assert_editor_state(
13978        &r#"fn one() {
13979            let mut a = two();
13980        }
13981
13982        fn «twoˇ»() {}"#
13983            .unindent(),
13984    );
13985    let editors = cx.update_workspace(|workspace, cx| {
13986        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13987    });
13988    cx.update_editor(|_, test_editor_cx| {
13989        assert_eq!(
13990            editors.len(),
13991            2,
13992            "After falling back to references search, we open a new editor with the results"
13993        );
13994        let references_fallback_text = editors
13995            .into_iter()
13996            .find(|new_editor| new_editor != test_editor_cx.view())
13997            .expect("Should have one non-test editor now")
13998            .read(test_editor_cx)
13999            .text(test_editor_cx);
14000        assert_eq!(
14001            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14002            "Should use the range from the references response and not the GoToDefinition one"
14003        );
14004    });
14005}
14006
14007#[gpui::test]
14008async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14009    init_test(cx, |_| {});
14010
14011    let language = Arc::new(Language::new(
14012        LanguageConfig::default(),
14013        Some(tree_sitter_rust::LANGUAGE.into()),
14014    ));
14015
14016    let text = r#"
14017        #[cfg(test)]
14018        mod tests() {
14019            #[test]
14020            fn runnable_1() {
14021                let a = 1;
14022            }
14023
14024            #[test]
14025            fn runnable_2() {
14026                let a = 1;
14027                let b = 2;
14028            }
14029        }
14030    "#
14031    .unindent();
14032
14033    let fs = FakeFs::new(cx.executor());
14034    fs.insert_file("/file.rs", Default::default()).await;
14035
14036    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14037    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14038    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14039    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
14040    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14041
14042    let editor = cx.new_view(|cx| {
14043        Editor::new(
14044            EditorMode::Full,
14045            multi_buffer,
14046            Some(project.clone()),
14047            true,
14048            cx,
14049        )
14050    });
14051
14052    editor.update(cx, |editor, cx| {
14053        editor.tasks.insert(
14054            (buffer.read(cx).remote_id(), 3),
14055            RunnableTasks {
14056                templates: vec![],
14057                offset: MultiBufferOffset(43),
14058                column: 0,
14059                extra_variables: HashMap::default(),
14060                context_range: BufferOffset(43)..BufferOffset(85),
14061            },
14062        );
14063        editor.tasks.insert(
14064            (buffer.read(cx).remote_id(), 8),
14065            RunnableTasks {
14066                templates: vec![],
14067                offset: MultiBufferOffset(86),
14068                column: 0,
14069                extra_variables: HashMap::default(),
14070                context_range: BufferOffset(86)..BufferOffset(191),
14071            },
14072        );
14073
14074        // Test finding task when cursor is inside function body
14075        editor.change_selections(None, cx, |s| {
14076            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14077        });
14078        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14079        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14080
14081        // Test finding task when cursor is on function name
14082        editor.change_selections(None, cx, |s| {
14083            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14084        });
14085        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14086        assert_eq!(row, 8, "Should find task when cursor is on function name");
14087    });
14088}
14089
14090#[gpui::test]
14091async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14092    init_test(cx, |_| {});
14093
14094    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14095    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14096    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14097
14098    let fs = FakeFs::new(cx.executor());
14099    fs.insert_tree(
14100        "/a",
14101        json!({
14102            "first.rs": sample_text_1,
14103            "second.rs": sample_text_2,
14104            "third.rs": sample_text_3,
14105        }),
14106    )
14107    .await;
14108    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14109    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14110    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14111    let worktree = project.update(cx, |project, cx| {
14112        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14113        assert_eq!(worktrees.len(), 1);
14114        worktrees.pop().unwrap()
14115    });
14116    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14117
14118    let buffer_1 = project
14119        .update(cx, |project, cx| {
14120            project.open_buffer((worktree_id, "first.rs"), cx)
14121        })
14122        .await
14123        .unwrap();
14124    let buffer_2 = project
14125        .update(cx, |project, cx| {
14126            project.open_buffer((worktree_id, "second.rs"), cx)
14127        })
14128        .await
14129        .unwrap();
14130    let buffer_3 = project
14131        .update(cx, |project, cx| {
14132            project.open_buffer((worktree_id, "third.rs"), cx)
14133        })
14134        .await
14135        .unwrap();
14136
14137    let multi_buffer = cx.new_model(|cx| {
14138        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14139        multi_buffer.push_excerpts(
14140            buffer_1.clone(),
14141            [
14142                ExcerptRange {
14143                    context: Point::new(0, 0)..Point::new(3, 0),
14144                    primary: None,
14145                },
14146                ExcerptRange {
14147                    context: Point::new(5, 0)..Point::new(7, 0),
14148                    primary: None,
14149                },
14150                ExcerptRange {
14151                    context: Point::new(9, 0)..Point::new(10, 4),
14152                    primary: None,
14153                },
14154            ],
14155            cx,
14156        );
14157        multi_buffer.push_excerpts(
14158            buffer_2.clone(),
14159            [
14160                ExcerptRange {
14161                    context: Point::new(0, 0)..Point::new(3, 0),
14162                    primary: None,
14163                },
14164                ExcerptRange {
14165                    context: Point::new(5, 0)..Point::new(7, 0),
14166                    primary: None,
14167                },
14168                ExcerptRange {
14169                    context: Point::new(9, 0)..Point::new(10, 4),
14170                    primary: None,
14171                },
14172            ],
14173            cx,
14174        );
14175        multi_buffer.push_excerpts(
14176            buffer_3.clone(),
14177            [
14178                ExcerptRange {
14179                    context: Point::new(0, 0)..Point::new(3, 0),
14180                    primary: None,
14181                },
14182                ExcerptRange {
14183                    context: Point::new(5, 0)..Point::new(7, 0),
14184                    primary: None,
14185                },
14186                ExcerptRange {
14187                    context: Point::new(9, 0)..Point::new(10, 4),
14188                    primary: None,
14189                },
14190            ],
14191            cx,
14192        );
14193        multi_buffer
14194    });
14195    let multi_buffer_editor = cx.new_view(|cx| {
14196        Editor::new(
14197            EditorMode::Full,
14198            multi_buffer,
14199            Some(project.clone()),
14200            true,
14201            cx,
14202        )
14203    });
14204
14205    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";
14206    assert_eq!(
14207        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14208        full_text,
14209    );
14210
14211    multi_buffer_editor.update(cx, |editor, cx| {
14212        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14213    });
14214    assert_eq!(
14215        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14216        "\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",
14217        "After folding the first buffer, its text should not be displayed"
14218    );
14219
14220    multi_buffer_editor.update(cx, |editor, cx| {
14221        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14222    });
14223    assert_eq!(
14224        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14225        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14226        "After folding the second buffer, its text should not be displayed"
14227    );
14228
14229    multi_buffer_editor.update(cx, |editor, cx| {
14230        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14231    });
14232    assert_eq!(
14233        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14234        "\n\n\n\n\n",
14235        "After folding the third buffer, its text should not be displayed"
14236    );
14237
14238    // Emulate selection inside the fold logic, that should work
14239    multi_buffer_editor.update(cx, |editor, cx| {
14240        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14241    });
14242
14243    multi_buffer_editor.update(cx, |editor, cx| {
14244        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14245    });
14246    assert_eq!(
14247        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14248        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14249        "After unfolding the second buffer, its text should be displayed"
14250    );
14251
14252    multi_buffer_editor.update(cx, |editor, cx| {
14253        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14254    });
14255    assert_eq!(
14256        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14257        "\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",
14258        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14259    );
14260
14261    multi_buffer_editor.update(cx, |editor, cx| {
14262        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14263    });
14264    assert_eq!(
14265        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14266        full_text,
14267        "After unfolding the all buffers, all original text should be displayed"
14268    );
14269}
14270
14271#[gpui::test]
14272async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14273    init_test(cx, |_| {});
14274
14275    let sample_text_1 = "1111\n2222\n3333".to_string();
14276    let sample_text_2 = "4444\n5555\n6666".to_string();
14277    let sample_text_3 = "7777\n8888\n9999".to_string();
14278
14279    let fs = FakeFs::new(cx.executor());
14280    fs.insert_tree(
14281        "/a",
14282        json!({
14283            "first.rs": sample_text_1,
14284            "second.rs": sample_text_2,
14285            "third.rs": sample_text_3,
14286        }),
14287    )
14288    .await;
14289    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14290    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14291    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14292    let worktree = project.update(cx, |project, cx| {
14293        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14294        assert_eq!(worktrees.len(), 1);
14295        worktrees.pop().unwrap()
14296    });
14297    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14298
14299    let buffer_1 = project
14300        .update(cx, |project, cx| {
14301            project.open_buffer((worktree_id, "first.rs"), cx)
14302        })
14303        .await
14304        .unwrap();
14305    let buffer_2 = project
14306        .update(cx, |project, cx| {
14307            project.open_buffer((worktree_id, "second.rs"), cx)
14308        })
14309        .await
14310        .unwrap();
14311    let buffer_3 = project
14312        .update(cx, |project, cx| {
14313            project.open_buffer((worktree_id, "third.rs"), cx)
14314        })
14315        .await
14316        .unwrap();
14317
14318    let multi_buffer = cx.new_model(|cx| {
14319        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14320        multi_buffer.push_excerpts(
14321            buffer_1.clone(),
14322            [ExcerptRange {
14323                context: Point::new(0, 0)..Point::new(3, 0),
14324                primary: None,
14325            }],
14326            cx,
14327        );
14328        multi_buffer.push_excerpts(
14329            buffer_2.clone(),
14330            [ExcerptRange {
14331                context: Point::new(0, 0)..Point::new(3, 0),
14332                primary: None,
14333            }],
14334            cx,
14335        );
14336        multi_buffer.push_excerpts(
14337            buffer_3.clone(),
14338            [ExcerptRange {
14339                context: Point::new(0, 0)..Point::new(3, 0),
14340                primary: None,
14341            }],
14342            cx,
14343        );
14344        multi_buffer
14345    });
14346
14347    let multi_buffer_editor = cx.new_view(|cx| {
14348        Editor::new(
14349            EditorMode::Full,
14350            multi_buffer,
14351            Some(project.clone()),
14352            true,
14353            cx,
14354        )
14355    });
14356
14357    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14358    assert_eq!(
14359        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14360        full_text,
14361    );
14362
14363    multi_buffer_editor.update(cx, |editor, cx| {
14364        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14365    });
14366    assert_eq!(
14367        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14368        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14369        "After folding the first buffer, its text should not be displayed"
14370    );
14371
14372    multi_buffer_editor.update(cx, |editor, cx| {
14373        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14374    });
14375
14376    assert_eq!(
14377        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14378        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14379        "After folding the second buffer, its text should not be displayed"
14380    );
14381
14382    multi_buffer_editor.update(cx, |editor, cx| {
14383        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14384    });
14385    assert_eq!(
14386        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14387        "\n\n\n\n\n",
14388        "After folding the third buffer, its text should not be displayed"
14389    );
14390
14391    multi_buffer_editor.update(cx, |editor, cx| {
14392        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14393    });
14394    assert_eq!(
14395        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14396        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14397        "After unfolding the second buffer, its text should be displayed"
14398    );
14399
14400    multi_buffer_editor.update(cx, |editor, cx| {
14401        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14402    });
14403    assert_eq!(
14404        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14405        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14406        "After unfolding the first buffer, its text should be displayed"
14407    );
14408
14409    multi_buffer_editor.update(cx, |editor, cx| {
14410        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14411    });
14412    assert_eq!(
14413        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14414        full_text,
14415        "After unfolding all buffers, all original text should be displayed"
14416    );
14417}
14418
14419#[gpui::test]
14420async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14421    init_test(cx, |_| {});
14422
14423    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14424
14425    let fs = FakeFs::new(cx.executor());
14426    fs.insert_tree(
14427        "/a",
14428        json!({
14429            "main.rs": sample_text,
14430        }),
14431    )
14432    .await;
14433    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14434    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14435    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14436    let worktree = project.update(cx, |project, cx| {
14437        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14438        assert_eq!(worktrees.len(), 1);
14439        worktrees.pop().unwrap()
14440    });
14441    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14442
14443    let buffer_1 = project
14444        .update(cx, |project, cx| {
14445            project.open_buffer((worktree_id, "main.rs"), cx)
14446        })
14447        .await
14448        .unwrap();
14449
14450    let multi_buffer = cx.new_model(|cx| {
14451        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14452        multi_buffer.push_excerpts(
14453            buffer_1.clone(),
14454            [ExcerptRange {
14455                context: Point::new(0, 0)
14456                    ..Point::new(
14457                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14458                        0,
14459                    ),
14460                primary: None,
14461            }],
14462            cx,
14463        );
14464        multi_buffer
14465    });
14466    let multi_buffer_editor = cx.new_view(|cx| {
14467        Editor::new(
14468            EditorMode::Full,
14469            multi_buffer,
14470            Some(project.clone()),
14471            true,
14472            cx,
14473        )
14474    });
14475
14476    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14477    multi_buffer_editor.update(cx, |editor, cx| {
14478        enum TestHighlight {}
14479        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14480        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14481        editor.highlight_text::<TestHighlight>(
14482            vec![highlight_range.clone()],
14483            HighlightStyle::color(Hsla::green()),
14484            cx,
14485        );
14486        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14487    });
14488
14489    let full_text = format!("\n\n\n{sample_text}\n");
14490    assert_eq!(
14491        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14492        full_text,
14493    );
14494}
14495
14496#[gpui::test]
14497fn test_inline_completion_text(cx: &mut TestAppContext) {
14498    init_test(cx, |_| {});
14499
14500    // Simple insertion
14501    {
14502        let window = cx.add_window(|cx| {
14503            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14504            Editor::new(EditorMode::Full, buffer, None, true, cx)
14505        });
14506        let cx = &mut VisualTestContext::from_window(*window, cx);
14507
14508        window
14509            .update(cx, |editor, cx| {
14510                let snapshot = editor.snapshot(cx);
14511                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14512                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14513                let edits = vec![(edit_range, " beautiful".to_string())];
14514
14515                let InlineCompletionText::Edit { text, highlights } =
14516                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14517                else {
14518                    panic!("Failed to generate inline completion text");
14519                };
14520
14521                assert_eq!(text, "Hello, beautiful world!");
14522                assert_eq!(highlights.len(), 1);
14523                assert_eq!(highlights[0].0, 6..16);
14524                assert_eq!(
14525                    highlights[0].1.background_color,
14526                    Some(cx.theme().status().created_background)
14527                );
14528            })
14529            .unwrap();
14530    }
14531
14532    // Replacement
14533    {
14534        let window = cx.add_window(|cx| {
14535            let buffer = MultiBuffer::build_simple("This is a test.", cx);
14536            Editor::new(EditorMode::Full, buffer, None, true, cx)
14537        });
14538        let cx = &mut VisualTestContext::from_window(*window, cx);
14539
14540        window
14541            .update(cx, |editor, cx| {
14542                let snapshot = editor.snapshot(cx);
14543                let edits = vec![(
14544                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14545                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
14546                    "That".to_string(),
14547                )];
14548
14549                let InlineCompletionText::Edit { text, highlights } =
14550                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14551                else {
14552                    panic!("Failed to generate inline completion text");
14553                };
14554
14555                assert_eq!(text, "That is a test.");
14556                assert_eq!(highlights.len(), 1);
14557                assert_eq!(highlights[0].0, 0..4);
14558                assert_eq!(
14559                    highlights[0].1.background_color,
14560                    Some(cx.theme().status().created_background)
14561                );
14562            })
14563            .unwrap();
14564    }
14565
14566    // Multiple edits
14567    {
14568        let window = cx.add_window(|cx| {
14569            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14570            Editor::new(EditorMode::Full, buffer, None, true, cx)
14571        });
14572        let cx = &mut VisualTestContext::from_window(*window, cx);
14573
14574        window
14575            .update(cx, |editor, cx| {
14576                let snapshot = editor.snapshot(cx);
14577                let edits = vec![
14578                    (
14579                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14580                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
14581                        "Greetings".into(),
14582                    ),
14583                    (
14584                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
14585                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
14586                        " and universe".into(),
14587                    ),
14588                ];
14589
14590                let InlineCompletionText::Edit { text, highlights } =
14591                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14592                else {
14593                    panic!("Failed to generate inline completion text");
14594                };
14595
14596                assert_eq!(text, "Greetings, world and universe!");
14597                assert_eq!(highlights.len(), 2);
14598                assert_eq!(highlights[0].0, 0..9);
14599                assert_eq!(highlights[1].0, 16..29);
14600                assert_eq!(
14601                    highlights[0].1.background_color,
14602                    Some(cx.theme().status().created_background)
14603                );
14604                assert_eq!(
14605                    highlights[1].1.background_color,
14606                    Some(cx.theme().status().created_background)
14607                );
14608            })
14609            .unwrap();
14610    }
14611
14612    // Multiple lines with edits
14613    {
14614        let window = cx.add_window(|cx| {
14615            let buffer =
14616                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
14617            Editor::new(EditorMode::Full, buffer, None, true, cx)
14618        });
14619        let cx = &mut VisualTestContext::from_window(*window, cx);
14620
14621        window
14622            .update(cx, |editor, cx| {
14623                let snapshot = editor.snapshot(cx);
14624                let edits = vec![
14625                    (
14626                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
14627                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
14628                        "modified".to_string(),
14629                    ),
14630                    (
14631                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
14632                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
14633                        "New third line".to_string(),
14634                    ),
14635                    (
14636                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
14637                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
14638                        " updated".to_string(),
14639                    ),
14640                ];
14641
14642                let InlineCompletionText::Edit { text, highlights } =
14643                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14644                else {
14645                    panic!("Failed to generate inline completion text");
14646                };
14647
14648                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
14649                assert_eq!(highlights.len(), 3);
14650                assert_eq!(highlights[0].0, 7..15); // "modified"
14651                assert_eq!(highlights[1].0, 16..30); // "New third line"
14652                assert_eq!(highlights[2].0, 37..45); // " updated"
14653
14654                for highlight in &highlights {
14655                    assert_eq!(
14656                        highlight.1.background_color,
14657                        Some(cx.theme().status().created_background)
14658                    );
14659                }
14660            })
14661            .unwrap();
14662    }
14663}
14664
14665#[gpui::test]
14666fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
14667    init_test(cx, |_| {});
14668
14669    // Deletion
14670    {
14671        let window = cx.add_window(|cx| {
14672            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14673            Editor::new(EditorMode::Full, buffer, None, true, cx)
14674        });
14675        let cx = &mut VisualTestContext::from_window(*window, cx);
14676
14677        window
14678            .update(cx, |editor, cx| {
14679                let snapshot = editor.snapshot(cx);
14680                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
14681                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
14682                let edits = vec![(edit_range, "".to_string())];
14683
14684                let InlineCompletionText::Edit { text, highlights } =
14685                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14686                else {
14687                    panic!("Failed to generate inline completion text");
14688                };
14689
14690                assert_eq!(text, "Hello, world!");
14691                assert_eq!(highlights.len(), 1);
14692                assert_eq!(highlights[0].0, 5..11);
14693                assert_eq!(
14694                    highlights[0].1.background_color,
14695                    Some(cx.theme().status().deleted_background)
14696                );
14697            })
14698            .unwrap();
14699    }
14700
14701    // Insertion
14702    {
14703        let window = cx.add_window(|cx| {
14704            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14705            Editor::new(EditorMode::Full, buffer, None, true, cx)
14706        });
14707        let cx = &mut VisualTestContext::from_window(*window, cx);
14708
14709        window
14710            .update(cx, |editor, cx| {
14711                let snapshot = editor.snapshot(cx);
14712                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14713                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14714                let edits = vec![(edit_range, " digital".to_string())];
14715
14716                let InlineCompletionText::Edit { text, highlights } =
14717                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14718                else {
14719                    panic!("Failed to generate inline completion text");
14720                };
14721
14722                assert_eq!(text, "Hello, digital world!");
14723                assert_eq!(highlights.len(), 1);
14724                assert_eq!(highlights[0].0, 6..14);
14725                assert_eq!(
14726                    highlights[0].1.background_color,
14727                    Some(cx.theme().status().created_background)
14728                );
14729            })
14730            .unwrap();
14731    }
14732}
14733
14734#[gpui::test]
14735async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
14736    init_test(cx, |_| {});
14737    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14738
14739    cx.set_state(indoc! {"
14740        struct Fˇoo {}
14741    "});
14742
14743    cx.update_editor(|editor, cx| {
14744        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
14745        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
14746        editor.highlight_background::<DocumentHighlightRead>(
14747            &[highlight_range],
14748            |c| c.editor_document_highlight_read_background,
14749            cx,
14750        );
14751    });
14752
14753    cx.update_editor(|e, cx| e.rename(&Rename, cx))
14754        .expect("Rename was not started")
14755        .await
14756        .expect("Rename failed");
14757    let mut rename_handler =
14758        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
14759            let edit = lsp::TextEdit {
14760                range: lsp::Range {
14761                    start: lsp::Position {
14762                        line: 0,
14763                        character: 7,
14764                    },
14765                    end: lsp::Position {
14766                        line: 0,
14767                        character: 10,
14768                    },
14769                },
14770                new_text: "FooRenamed".to_string(),
14771            };
14772            Ok(Some(lsp::WorkspaceEdit::new(
14773                // Specify the same edit twice
14774                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
14775            )))
14776        });
14777    cx.update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
14778        .expect("Confirm rename was not started")
14779        .await
14780        .expect("Confirm rename failed");
14781    rename_handler.next().await.unwrap();
14782    cx.run_until_parked();
14783
14784    // Despite two edits, only one is actually applied as those are identical
14785    cx.assert_editor_state(indoc! {"
14786        struct FooRenamedˇ {}
14787    "});
14788}
14789
14790fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
14791    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
14792    point..point
14793}
14794
14795fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
14796    let (text, ranges) = marked_text_ranges(marked_text, true);
14797    assert_eq!(view.text(cx), text);
14798    assert_eq!(
14799        view.selections.ranges(cx),
14800        ranges,
14801        "Assert selections are {}",
14802        marked_text
14803    );
14804}
14805
14806pub fn handle_signature_help_request(
14807    cx: &mut EditorLspTestContext,
14808    mocked_response: lsp::SignatureHelp,
14809) -> impl Future<Output = ()> {
14810    let mut request =
14811        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
14812            let mocked_response = mocked_response.clone();
14813            async move { Ok(Some(mocked_response)) }
14814        });
14815
14816    async move {
14817        request.next().await;
14818    }
14819}
14820
14821/// Handle completion request passing a marked string specifying where the completion
14822/// should be triggered from using '|' character, what range should be replaced, and what completions
14823/// should be returned using '<' and '>' to delimit the range
14824pub fn handle_completion_request(
14825    cx: &mut EditorLspTestContext,
14826    marked_string: &str,
14827    completions: Vec<&'static str>,
14828    counter: Arc<AtomicUsize>,
14829) -> impl Future<Output = ()> {
14830    let complete_from_marker: TextRangeMarker = '|'.into();
14831    let replace_range_marker: TextRangeMarker = ('<', '>').into();
14832    let (_, mut marked_ranges) = marked_text_ranges_by(
14833        marked_string,
14834        vec![complete_from_marker.clone(), replace_range_marker.clone()],
14835    );
14836
14837    let complete_from_position =
14838        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
14839    let replace_range =
14840        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
14841
14842    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
14843        let completions = completions.clone();
14844        counter.fetch_add(1, atomic::Ordering::Release);
14845        async move {
14846            assert_eq!(params.text_document_position.text_document.uri, url.clone());
14847            assert_eq!(
14848                params.text_document_position.position,
14849                complete_from_position
14850            );
14851            Ok(Some(lsp::CompletionResponse::Array(
14852                completions
14853                    .iter()
14854                    .map(|completion_text| lsp::CompletionItem {
14855                        label: completion_text.to_string(),
14856                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14857                            range: replace_range,
14858                            new_text: completion_text.to_string(),
14859                        })),
14860                        ..Default::default()
14861                    })
14862                    .collect(),
14863            )))
14864        }
14865    });
14866
14867    async move {
14868        request.next().await;
14869    }
14870}
14871
14872fn handle_resolve_completion_request(
14873    cx: &mut EditorLspTestContext,
14874    edits: Option<Vec<(&'static str, &'static str)>>,
14875) -> impl Future<Output = ()> {
14876    let edits = edits.map(|edits| {
14877        edits
14878            .iter()
14879            .map(|(marked_string, new_text)| {
14880                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
14881                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
14882                lsp::TextEdit::new(replace_range, new_text.to_string())
14883            })
14884            .collect::<Vec<_>>()
14885    });
14886
14887    let mut request =
14888        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14889            let edits = edits.clone();
14890            async move {
14891                Ok(lsp::CompletionItem {
14892                    additional_text_edits: edits,
14893                    ..Default::default()
14894                })
14895            }
14896        });
14897
14898    async move {
14899        request.next().await;
14900    }
14901}
14902
14903pub(crate) fn update_test_language_settings(
14904    cx: &mut TestAppContext,
14905    f: impl Fn(&mut AllLanguageSettingsContent),
14906) {
14907    cx.update(|cx| {
14908        SettingsStore::update_global(cx, |store, cx| {
14909            store.update_user_settings::<AllLanguageSettings>(cx, f);
14910        });
14911    });
14912}
14913
14914pub(crate) fn update_test_project_settings(
14915    cx: &mut TestAppContext,
14916    f: impl Fn(&mut ProjectSettings),
14917) {
14918    cx.update(|cx| {
14919        SettingsStore::update_global(cx, |store, cx| {
14920            store.update_user_settings::<ProjectSettings>(cx, f);
14921        });
14922    });
14923}
14924
14925pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14926    cx.update(|cx| {
14927        assets::Assets.load_test_fonts(cx);
14928        let store = SettingsStore::test(cx);
14929        cx.set_global(store);
14930        theme::init(theme::LoadThemes::JustBase, cx);
14931        release_channel::init(SemanticVersion::default(), cx);
14932        client::init_settings(cx);
14933        language::init(cx);
14934        Project::init_settings(cx);
14935        workspace::init_settings(cx);
14936        crate::init(cx);
14937    });
14938
14939    update_test_language_settings(cx, f);
14940}
14941
14942#[track_caller]
14943fn assert_hunk_revert(
14944    not_reverted_text_with_selections: &str,
14945    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14946    expected_reverted_text_with_selections: &str,
14947    base_text: &str,
14948    cx: &mut EditorLspTestContext,
14949) {
14950    cx.set_state(not_reverted_text_with_selections);
14951    cx.set_diff_base(base_text);
14952    cx.executor().run_until_parked();
14953
14954    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14955        let snapshot = editor.snapshot(cx);
14956        let reverted_hunk_statuses = snapshot
14957            .diff_map
14958            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14959            .map(|hunk| hunk_status(&hunk))
14960            .collect::<Vec<_>>();
14961
14962        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14963        reverted_hunk_statuses
14964    });
14965    cx.executor().run_until_parked();
14966    cx.assert_editor_state(expected_reverted_text_with_selections);
14967    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14968}