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