editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45};
   46use workspace::{
   47    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   48    NavigationEntry, ViewId,
   49};
   50
   51#[gpui::test]
   52fn test_edit_events(cx: &mut TestAppContext) {
   53    init_test(cx, |_| {});
   54
   55    let buffer = cx.new_model(|cx| {
   56        let mut buffer = language::Buffer::local("123456", cx);
   57        buffer.set_group_interval(Duration::from_secs(1));
   58        buffer
   59    });
   60
   61    let events = Rc::new(RefCell::new(Vec::new()));
   62    let editor1 = cx.add_window({
   63        let events = events.clone();
   64        |cx| {
   65            let view = cx.view().clone();
   66            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   67                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   68                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   69                _ => {}
   70            })
   71            .detach();
   72            Editor::for_buffer(buffer.clone(), None, cx)
   73        }
   74    });
   75
   76    let editor2 = cx.add_window({
   77        let events = events.clone();
   78        |cx| {
   79            cx.subscribe(
   80                &cx.view().clone(),
   81                move |_, _, event: &EditorEvent, _| match event {
   82                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   83                    EditorEvent::BufferEdited => {
   84                        events.borrow_mut().push(("editor2", "buffer edited"))
   85                    }
   86                    _ => {}
   87                },
   88            )
   89            .detach();
   90            Editor::for_buffer(buffer.clone(), None, cx)
   91        }
   92    });
   93
   94    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   95
   96    // Mutating editor 1 will emit an `Edited` event only for that editor.
   97    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   98    assert_eq!(
   99        mem::take(&mut *events.borrow_mut()),
  100        [
  101            ("editor1", "edited"),
  102            ("editor1", "buffer edited"),
  103            ("editor2", "buffer edited"),
  104        ]
  105    );
  106
  107    // Mutating editor 2 will emit an `Edited` event only for that editor.
  108    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  109    assert_eq!(
  110        mem::take(&mut *events.borrow_mut()),
  111        [
  112            ("editor2", "edited"),
  113            ("editor1", "buffer edited"),
  114            ("editor2", "buffer edited"),
  115        ]
  116    );
  117
  118    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  119    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  120    assert_eq!(
  121        mem::take(&mut *events.borrow_mut()),
  122        [
  123            ("editor1", "edited"),
  124            ("editor1", "buffer edited"),
  125            ("editor2", "buffer edited"),
  126        ]
  127    );
  128
  129    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  130    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  131    assert_eq!(
  132        mem::take(&mut *events.borrow_mut()),
  133        [
  134            ("editor1", "edited"),
  135            ("editor1", "buffer edited"),
  136            ("editor2", "buffer edited"),
  137        ]
  138    );
  139
  140    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  141    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  142    assert_eq!(
  143        mem::take(&mut *events.borrow_mut()),
  144        [
  145            ("editor2", "edited"),
  146            ("editor1", "buffer edited"),
  147            ("editor2", "buffer edited"),
  148        ]
  149    );
  150
  151    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  152    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  153    assert_eq!(
  154        mem::take(&mut *events.borrow_mut()),
  155        [
  156            ("editor2", "edited"),
  157            ("editor1", "buffer edited"),
  158            ("editor2", "buffer edited"),
  159        ]
  160    );
  161
  162    // No event is emitted when the mutation is a no-op.
  163    _ = editor2.update(cx, |editor, cx| {
  164        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  165
  166        editor.backspace(&Backspace, cx);
  167    });
  168    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  169}
  170
  171#[gpui::test]
  172fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  173    init_test(cx, |_| {});
  174
  175    let mut now = Instant::now();
  176    let group_interval = Duration::from_millis(1);
  177    let buffer = cx.new_model(|cx| {
  178        let mut buf = language::Buffer::local("123456", cx);
  179        buf.set_group_interval(group_interval);
  180        buf
  181    });
  182    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  183    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  184
  185    _ = editor.update(cx, |editor, cx| {
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  188
  189        editor.insert("cd", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cd56");
  192        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  193
  194        editor.start_transaction_at(now, cx);
  195        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  196        editor.insert("e", cx);
  197        editor.end_transaction_at(now, cx);
  198        assert_eq!(editor.text(cx), "12cde6");
  199        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  200
  201        now += group_interval + Duration::from_millis(1);
  202        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  203
  204        // Simulate an edit in another editor
  205        buffer.update(cx, |buffer, cx| {
  206            buffer.start_transaction_at(now, cx);
  207            buffer.edit([(0..1, "a")], None, cx);
  208            buffer.edit([(1..1, "b")], None, cx);
  209            buffer.end_transaction_at(now, cx);
  210        });
  211
  212        assert_eq!(editor.text(cx), "ab2cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  214
  215        // Last transaction happened past the group interval in a different editor.
  216        // Undo it individually and don't restore selections.
  217        editor.undo(&Undo, cx);
  218        assert_eq!(editor.text(cx), "12cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  220
  221        // First two transactions happened within the group interval in this editor.
  222        // Undo them together and restore selections.
  223        editor.undo(&Undo, cx);
  224        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  225        assert_eq!(editor.text(cx), "123456");
  226        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  227
  228        // Redo the first two transactions together.
  229        editor.redo(&Redo, cx);
  230        assert_eq!(editor.text(cx), "12cde6");
  231        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  232
  233        // Redo the last transaction on its own.
  234        editor.redo(&Redo, cx);
  235        assert_eq!(editor.text(cx), "ab2cde6");
  236        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  237
  238        // Test empty transactions.
  239        editor.start_transaction_at(now, cx);
  240        editor.end_transaction_at(now, cx);
  241        editor.undo(&Undo, cx);
  242        assert_eq!(editor.text(cx), "12cde6");
  243    });
  244}
  245
  246#[gpui::test]
  247fn test_ime_composition(cx: &mut TestAppContext) {
  248    init_test(cx, |_| {});
  249
  250    let buffer = cx.new_model(|cx| {
  251        let mut buffer = language::Buffer::local("abcde", cx);
  252        // Ensure automatic grouping doesn't occur.
  253        buffer.set_group_interval(Duration::ZERO);
  254        buffer
  255    });
  256
  257    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  258    cx.add_window(|cx| {
  259        let mut editor = build_editor(buffer.clone(), cx);
  260
  261        // Start a new IME composition.
  262        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  263        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  264        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  265        assert_eq!(editor.text(cx), "äbcde");
  266        assert_eq!(
  267            editor.marked_text_ranges(cx),
  268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  269        );
  270
  271        // Finalize IME composition.
  272        editor.replace_text_in_range(None, "ā", cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // IME composition edits are grouped and are undone/redone at once.
  277        editor.undo(&Default::default(), cx);
  278        assert_eq!(editor.text(cx), "abcde");
  279        assert_eq!(editor.marked_text_ranges(cx), None);
  280        editor.redo(&Default::default(), cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // Start a new IME composition.
  285        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  286        assert_eq!(
  287            editor.marked_text_ranges(cx),
  288            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  289        );
  290
  291        // Undoing during an IME composition cancels it.
  292        editor.undo(&Default::default(), cx);
  293        assert_eq!(editor.text(cx), "ābcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295
  296        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  297        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  298        assert_eq!(editor.text(cx), "ābcdè");
  299        assert_eq!(
  300            editor.marked_text_ranges(cx),
  301            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  302        );
  303
  304        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  305        editor.replace_text_in_range(Some(4..999), "ę", cx);
  306        assert_eq!(editor.text(cx), "ābcdę");
  307        assert_eq!(editor.marked_text_ranges(cx), None);
  308
  309        // Start a new IME composition with multiple cursors.
  310        editor.change_selections(None, cx, |s| {
  311            s.select_ranges([
  312                OffsetUtf16(1)..OffsetUtf16(1),
  313                OffsetUtf16(3)..OffsetUtf16(3),
  314                OffsetUtf16(5)..OffsetUtf16(5),
  315            ])
  316        });
  317        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  318        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  319        assert_eq!(
  320            editor.marked_text_ranges(cx),
  321            Some(vec![
  322                OffsetUtf16(0)..OffsetUtf16(3),
  323                OffsetUtf16(4)..OffsetUtf16(7),
  324                OffsetUtf16(8)..OffsetUtf16(11)
  325            ])
  326        );
  327
  328        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  329        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  330        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  331        assert_eq!(
  332            editor.marked_text_ranges(cx),
  333            Some(vec![
  334                OffsetUtf16(1)..OffsetUtf16(2),
  335                OffsetUtf16(5)..OffsetUtf16(6),
  336                OffsetUtf16(9)..OffsetUtf16(10)
  337            ])
  338        );
  339
  340        // Finalize IME composition with multiple cursors.
  341        editor.replace_text_in_range(Some(9..10), "2", cx);
  342        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  343        assert_eq!(editor.marked_text_ranges(cx), None);
  344
  345        editor
  346    });
  347}
  348
  349#[gpui::test]
  350fn test_selection_with_mouse(cx: &mut TestAppContext) {
  351    init_test(cx, |_| {});
  352
  353    let editor = cx.add_window(|cx| {
  354        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  355        build_editor(buffer, cx)
  356    });
  357
  358    _ = editor.update(cx, |view, cx| {
  359        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  360    });
  361    assert_eq!(
  362        editor
  363            .update(cx, |view, cx| view.selections.display_ranges(cx))
  364            .unwrap(),
  365        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  366    );
  367
  368    _ = editor.update(cx, |view, cx| {
  369        view.update_selection(
  370            DisplayPoint::new(DisplayRow(3), 3),
  371            0,
  372            gpui::Point::<f32>::default(),
  373            cx,
  374        );
  375    });
  376
  377    assert_eq!(
  378        editor
  379            .update(cx, |view, cx| view.selections.display_ranges(cx))
  380            .unwrap(),
  381        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  382    );
  383
  384    _ = editor.update(cx, |view, cx| {
  385        view.update_selection(
  386            DisplayPoint::new(DisplayRow(1), 1),
  387            0,
  388            gpui::Point::<f32>::default(),
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |view, cx| view.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  398    );
  399
  400    _ = editor.update(cx, |view, cx| {
  401        view.end_selection(cx);
  402        view.update_selection(
  403            DisplayPoint::new(DisplayRow(3), 3),
  404            0,
  405            gpui::Point::<f32>::default(),
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |view, cx| view.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |view, cx| {
  418        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  419        view.update_selection(
  420            DisplayPoint::new(DisplayRow(0), 0),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |view, cx| view.selections.display_ranges(cx))
  430            .unwrap(),
  431        [
  432            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  433            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  434        ]
  435    );
  436
  437    _ = editor.update(cx, |view, cx| {
  438        view.end_selection(cx);
  439    });
  440
  441    assert_eq!(
  442        editor
  443            .update(cx, |view, cx| view.selections.display_ranges(cx))
  444            .unwrap(),
  445        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  446    );
  447}
  448
  449#[gpui::test]
  450fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  451    init_test(cx, |_| {});
  452
  453    let editor = cx.add_window(|cx| {
  454        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  455        build_editor(buffer, cx)
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  468    });
  469
  470    _ = editor.update(cx, |view, cx| {
  471        view.end_selection(cx);
  472    });
  473
  474    assert_eq!(
  475        editor
  476            .update(cx, |view, cx| view.selections.display_ranges(cx))
  477            .unwrap(),
  478        [
  479            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  480            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  481        ]
  482    );
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  486    });
  487
  488    _ = editor.update(cx, |view, cx| {
  489        view.end_selection(cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |view, cx| view.selections.display_ranges(cx))
  495            .unwrap(),
  496        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  497    );
  498}
  499
  500#[gpui::test]
  501fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  502    init_test(cx, |_| {});
  503
  504    let view = cx.add_window(|cx| {
  505        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  506        build_editor(buffer, cx)
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  511        assert_eq!(
  512            view.selections.display_ranges(cx),
  513            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  514        );
  515    });
  516
  517    _ = view.update(cx, |view, cx| {
  518        view.update_selection(
  519            DisplayPoint::new(DisplayRow(3), 3),
  520            0,
  521            gpui::Point::<f32>::default(),
  522            cx,
  523        );
  524        assert_eq!(
  525            view.selections.display_ranges(cx),
  526            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  527        );
  528    });
  529
  530    _ = view.update(cx, |view, cx| {
  531        view.cancel(&Cancel, cx);
  532        view.update_selection(
  533            DisplayPoint::new(DisplayRow(1), 1),
  534            0,
  535            gpui::Point::<f32>::default(),
  536            cx,
  537        );
  538        assert_eq!(
  539            view.selections.display_ranges(cx),
  540            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  541        );
  542    });
  543}
  544
  545#[gpui::test]
  546fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  547    init_test(cx, |_| {});
  548
  549    let view = cx.add_window(|cx| {
  550        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  551        build_editor(buffer, cx)
  552    });
  553
  554    _ = view.update(cx, |view, cx| {
  555        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  556        assert_eq!(
  557            view.selections.display_ranges(cx),
  558            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  559        );
  560
  561        view.move_down(&Default::default(), cx);
  562        assert_eq!(
  563            view.selections.display_ranges(cx),
  564            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  565        );
  566
  567        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  568        assert_eq!(
  569            view.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        view.move_up(&Default::default(), cx);
  574        assert_eq!(
  575            view.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  577        );
  578    });
  579}
  580
  581#[gpui::test]
  582fn test_clone(cx: &mut TestAppContext) {
  583    init_test(cx, |_| {});
  584
  585    let (text, selection_ranges) = marked_text_ranges(
  586        indoc! {"
  587            one
  588            two
  589            threeˇ
  590            four
  591            fiveˇ
  592        "},
  593        true,
  594    );
  595
  596    let editor = cx.add_window(|cx| {
  597        let buffer = MultiBuffer::build_simple(&text, cx);
  598        build_editor(buffer, cx)
  599    });
  600
  601    _ = editor.update(cx, |editor, cx| {
  602        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  603        editor.fold_creases(
  604            vec![
  605                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  606                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  607            ],
  608            true,
  609            cx,
  610        );
  611    });
  612
  613    let cloned_editor = editor
  614        .update(cx, |editor, cx| {
  615            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  616        })
  617        .unwrap()
  618        .unwrap();
  619
  620    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  621    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  622
  623    assert_eq!(
  624        cloned_editor
  625            .update(cx, |e, cx| e.display_text(cx))
  626            .unwrap(),
  627        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  628    );
  629    assert_eq!(
  630        cloned_snapshot
  631            .folds_in_range(0..text.len())
  632            .collect::<Vec<_>>(),
  633        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |editor, cx| editor.selections.ranges(cx))
  641            .unwrap()
  642    );
  643    assert_set_eq!(
  644        cloned_editor
  645            .update(cx, |e, cx| e.selections.display_ranges(cx))
  646            .unwrap(),
  647        editor
  648            .update(cx, |e, cx| e.selections.display_ranges(cx))
  649            .unwrap()
  650    );
  651}
  652
  653#[gpui::test]
  654async fn test_navigation_history(cx: &mut TestAppContext) {
  655    init_test(cx, |_| {});
  656
  657    use workspace::item::Item;
  658
  659    let fs = FakeFs::new(cx.executor());
  660    let project = Project::test(fs, [], cx).await;
  661    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  662    let pane = workspace
  663        .update(cx, |workspace, _| workspace.active_pane().clone())
  664        .unwrap();
  665
  666    _ = workspace.update(cx, |_v, cx| {
  667        cx.new_view(|cx| {
  668            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  669            let mut editor = build_editor(buffer.clone(), cx);
  670            let handle = cx.view();
  671            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  672
  673            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  674                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  675            }
  676
  677            // Move the cursor a small distance.
  678            // Nothing is added to the navigation history.
  679            editor.change_selections(None, cx, |s| {
  680                s.select_display_ranges([
  681                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  682                ])
  683            });
  684            editor.change_selections(None, cx, |s| {
  685                s.select_display_ranges([
  686                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  687                ])
  688            });
  689            assert!(pop_history(&mut editor, cx).is_none());
  690
  691            // Move the cursor a large distance.
  692            // The history can jump back to the previous position.
  693            editor.change_selections(None, cx, |s| {
  694                s.select_display_ranges([
  695                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  696                ])
  697            });
  698            let nav_entry = pop_history(&mut editor, cx).unwrap();
  699            editor.navigate(nav_entry.data.unwrap(), cx);
  700            assert_eq!(nav_entry.item.id(), cx.entity_id());
  701            assert_eq!(
  702                editor.selections.display_ranges(cx),
  703                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  704            );
  705            assert!(pop_history(&mut editor, cx).is_none());
  706
  707            // Move the cursor a small distance via the mouse.
  708            // Nothing is added to the navigation history.
  709            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  710            editor.end_selection(cx);
  711            assert_eq!(
  712                editor.selections.display_ranges(cx),
  713                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  714            );
  715            assert!(pop_history(&mut editor, cx).is_none());
  716
  717            // Move the cursor a large distance via the mouse.
  718            // The history can jump back to the previous position.
  719            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  720            editor.end_selection(cx);
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  724            );
  725            let nav_entry = pop_history(&mut editor, cx).unwrap();
  726            editor.navigate(nav_entry.data.unwrap(), cx);
  727            assert_eq!(nav_entry.item.id(), cx.entity_id());
  728            assert_eq!(
  729                editor.selections.display_ranges(cx),
  730                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  731            );
  732            assert!(pop_history(&mut editor, cx).is_none());
  733
  734            // Set scroll position to check later
  735            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  736            let original_scroll_position = editor.scroll_manager.anchor();
  737
  738            // Jump to the end of the document and adjust scroll
  739            editor.move_to_end(&MoveToEnd, cx);
  740            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  741            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  742
  743            let nav_entry = pop_history(&mut editor, cx).unwrap();
  744            editor.navigate(nav_entry.data.unwrap(), cx);
  745            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  746
  747            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  748            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  749            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  750            let invalid_point = Point::new(9999, 0);
  751            editor.navigate(
  752                Box::new(NavigationData {
  753                    cursor_anchor: invalid_anchor,
  754                    cursor_position: invalid_point,
  755                    scroll_anchor: ScrollAnchor {
  756                        anchor: invalid_anchor,
  757                        offset: Default::default(),
  758                    },
  759                    scroll_top_row: invalid_point.row,
  760                }),
  761                cx,
  762            );
  763            assert_eq!(
  764                editor.selections.display_ranges(cx),
  765                &[editor.max_point(cx)..editor.max_point(cx)]
  766            );
  767            assert_eq!(
  768                editor.scroll_position(cx),
  769                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  770            );
  771
  772            editor
  773        })
  774    });
  775}
  776
  777#[gpui::test]
  778fn test_cancel(cx: &mut TestAppContext) {
  779    init_test(cx, |_| {});
  780
  781    let view = cx.add_window(|cx| {
  782        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  783        build_editor(buffer, cx)
  784    });
  785
  786    _ = view.update(cx, |view, cx| {
  787        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  788        view.update_selection(
  789            DisplayPoint::new(DisplayRow(1), 1),
  790            0,
  791            gpui::Point::<f32>::default(),
  792            cx,
  793        );
  794        view.end_selection(cx);
  795
  796        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  797        view.update_selection(
  798            DisplayPoint::new(DisplayRow(0), 3),
  799            0,
  800            gpui::Point::<f32>::default(),
  801            cx,
  802        );
  803        view.end_selection(cx);
  804        assert_eq!(
  805            view.selections.display_ranges(cx),
  806            [
  807                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  808                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  809            ]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820
  821    _ = view.update(cx, |view, cx| {
  822        view.cancel(&Cancel, cx);
  823        assert_eq!(
  824            view.selections.display_ranges(cx),
  825            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  826        );
  827    });
  828}
  829
  830#[gpui::test]
  831fn test_fold_action(cx: &mut TestAppContext) {
  832    init_test(cx, |_| {});
  833
  834    let view = cx.add_window(|cx| {
  835        let buffer = MultiBuffer::build_simple(
  836            &"
  837                impl Foo {
  838                    // Hello!
  839
  840                    fn a() {
  841                        1
  842                    }
  843
  844                    fn b() {
  845                        2
  846                    }
  847
  848                    fn c() {
  849                        3
  850                    }
  851                }
  852            "
  853            .unindent(),
  854            cx,
  855        );
  856        build_editor(buffer.clone(), cx)
  857    });
  858
  859    _ = view.update(cx, |view, cx| {
  860        view.change_selections(None, cx, |s| {
  861            s.select_display_ranges([
  862                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  863            ]);
  864        });
  865        view.fold(&Fold, cx);
  866        assert_eq!(
  867            view.display_text(cx),
  868            "
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {⋯
  877                    }
  878
  879                    fn c() {⋯
  880                    }
  881                }
  882            "
  883            .unindent(),
  884        );
  885
  886        view.fold(&Fold, cx);
  887        assert_eq!(
  888            view.display_text(cx),
  889            "
  890                impl Foo {⋯
  891                }
  892            "
  893            .unindent(),
  894        );
  895
  896        view.unfold_lines(&UnfoldLines, cx);
  897        assert_eq!(
  898            view.display_text(cx),
  899            "
  900                impl Foo {
  901                    // Hello!
  902
  903                    fn a() {
  904                        1
  905                    }
  906
  907                    fn b() {⋯
  908                    }
  909
  910                    fn c() {⋯
  911                    }
  912                }
  913            "
  914            .unindent(),
  915        );
  916
  917        view.unfold_lines(&UnfoldLines, cx);
  918        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  919    });
  920}
  921
  922#[gpui::test]
  923fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  924    init_test(cx, |_| {});
  925
  926    let view = cx.add_window(|cx| {
  927        let buffer = MultiBuffer::build_simple(
  928            &"
  929                class Foo:
  930                    # Hello!
  931
  932                    def a():
  933                        print(1)
  934
  935                    def b():
  936                        print(2)
  937
  938                    def c():
  939                        print(3)
  940            "
  941            .unindent(),
  942            cx,
  943        );
  944        build_editor(buffer.clone(), cx)
  945    });
  946
  947    _ = view.update(cx, |view, cx| {
  948        view.change_selections(None, cx, |s| {
  949            s.select_display_ranges([
  950                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  951            ]);
  952        });
  953        view.fold(&Fold, cx);
  954        assert_eq!(
  955            view.display_text(cx),
  956            "
  957                class Foo:
  958                    # Hello!
  959
  960                    def a():
  961                        print(1)
  962
  963                    def b():⋯
  964
  965                    def c():⋯
  966            "
  967            .unindent(),
  968        );
  969
  970        view.fold(&Fold, cx);
  971        assert_eq!(
  972            view.display_text(cx),
  973            "
  974                class Foo:⋯
  975            "
  976            .unindent(),
  977        );
  978
  979        view.unfold_lines(&UnfoldLines, cx);
  980        assert_eq!(
  981            view.display_text(cx),
  982            "
  983                class Foo:
  984                    # Hello!
  985
  986                    def a():
  987                        print(1)
  988
  989                    def b():⋯
  990
  991                    def c():⋯
  992            "
  993            .unindent(),
  994        );
  995
  996        view.unfold_lines(&UnfoldLines, cx);
  997        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  998    });
  999}
 1000
 1001#[gpui::test]
 1002fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1003    init_test(cx, |_| {});
 1004
 1005    let view = cx.add_window(|cx| {
 1006        let buffer = MultiBuffer::build_simple(
 1007            &"
 1008                class Foo:
 1009                    # Hello!
 1010
 1011                    def a():
 1012                        print(1)
 1013
 1014                    def b():
 1015                        print(2)
 1016
 1017
 1018                    def c():
 1019                        print(3)
 1020
 1021
 1022            "
 1023            .unindent(),
 1024            cx,
 1025        );
 1026        build_editor(buffer.clone(), cx)
 1027    });
 1028
 1029    _ = view.update(cx, |view, cx| {
 1030        view.change_selections(None, cx, |s| {
 1031            s.select_display_ranges([
 1032                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1033            ]);
 1034        });
 1035        view.fold(&Fold, cx);
 1036        assert_eq!(
 1037            view.display_text(cx),
 1038            "
 1039                class Foo:
 1040                    # Hello!
 1041
 1042                    def a():
 1043                        print(1)
 1044
 1045                    def b():⋯
 1046
 1047
 1048                    def c():⋯
 1049
 1050
 1051            "
 1052            .unindent(),
 1053        );
 1054
 1055        view.fold(&Fold, cx);
 1056        assert_eq!(
 1057            view.display_text(cx),
 1058            "
 1059                class Foo:⋯
 1060
 1061
 1062            "
 1063            .unindent(),
 1064        );
 1065
 1066        view.unfold_lines(&UnfoldLines, cx);
 1067        assert_eq!(
 1068            view.display_text(cx),
 1069            "
 1070                class Foo:
 1071                    # Hello!
 1072
 1073                    def a():
 1074                        print(1)
 1075
 1076                    def b():⋯
 1077
 1078
 1079                    def c():⋯
 1080
 1081
 1082            "
 1083            .unindent(),
 1084        );
 1085
 1086        view.unfold_lines(&UnfoldLines, cx);
 1087        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1088    });
 1089}
 1090
 1091#[gpui::test]
 1092fn test_fold_at_level(cx: &mut TestAppContext) {
 1093    init_test(cx, |_| {});
 1094
 1095    let view = cx.add_window(|cx| {
 1096        let buffer = MultiBuffer::build_simple(
 1097            &"
 1098                class Foo:
 1099                    # Hello!
 1100
 1101                    def a():
 1102                        print(1)
 1103
 1104                    def b():
 1105                        print(2)
 1106
 1107
 1108                class Bar:
 1109                    # World!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():
 1115                        print(2)
 1116
 1117
 1118            "
 1119            .unindent(),
 1120            cx,
 1121        );
 1122        build_editor(buffer.clone(), cx)
 1123    });
 1124
 1125    _ = view.update(cx, |view, cx| {
 1126        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1127        assert_eq!(
 1128            view.display_text(cx),
 1129            "
 1130                class Foo:
 1131                    # Hello!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138                class Bar:
 1139                    # World!
 1140
 1141                    def a():⋯
 1142
 1143                    def b():⋯
 1144
 1145
 1146            "
 1147            .unindent(),
 1148        );
 1149
 1150        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1151        assert_eq!(
 1152            view.display_text(cx),
 1153            "
 1154                class Foo:⋯
 1155
 1156
 1157                class Bar:⋯
 1158
 1159
 1160            "
 1161            .unindent(),
 1162        );
 1163
 1164        view.unfold_all(&UnfoldAll, cx);
 1165        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1166        assert_eq!(
 1167            view.display_text(cx),
 1168            "
 1169                class Foo:
 1170                    # Hello!
 1171
 1172                    def a():
 1173                        print(1)
 1174
 1175                    def b():
 1176                        print(2)
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():
 1183                        print(1)
 1184
 1185                    def b():
 1186                        print(2)
 1187
 1188
 1189            "
 1190            .unindent(),
 1191        );
 1192
 1193        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1194    });
 1195}
 1196
 1197#[gpui::test]
 1198fn test_move_cursor(cx: &mut TestAppContext) {
 1199    init_test(cx, |_| {});
 1200
 1201    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1202    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1203
 1204    buffer.update(cx, |buffer, cx| {
 1205        buffer.edit(
 1206            vec![
 1207                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1208                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1209            ],
 1210            None,
 1211            cx,
 1212        );
 1213    });
 1214    _ = view.update(cx, |view, cx| {
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1218        );
 1219
 1220        view.move_down(&MoveDown, cx);
 1221        assert_eq!(
 1222            view.selections.display_ranges(cx),
 1223            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1224        );
 1225
 1226        view.move_right(&MoveRight, cx);
 1227        assert_eq!(
 1228            view.selections.display_ranges(cx),
 1229            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1230        );
 1231
 1232        view.move_left(&MoveLeft, cx);
 1233        assert_eq!(
 1234            view.selections.display_ranges(cx),
 1235            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1236        );
 1237
 1238        view.move_up(&MoveUp, cx);
 1239        assert_eq!(
 1240            view.selections.display_ranges(cx),
 1241            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1242        );
 1243
 1244        view.move_to_end(&MoveToEnd, cx);
 1245        assert_eq!(
 1246            view.selections.display_ranges(cx),
 1247            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1248        );
 1249
 1250        view.move_to_beginning(&MoveToBeginning, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        view.change_selections(None, cx, |s| {
 1257            s.select_display_ranges([
 1258                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1259            ]);
 1260        });
 1261        view.select_to_beginning(&SelectToBeginning, cx);
 1262        assert_eq!(
 1263            view.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1265        );
 1266
 1267        view.select_to_end(&SelectToEnd, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1271        );
 1272    });
 1273}
 1274
 1275// TODO: Re-enable this test
 1276#[cfg(target_os = "macos")]
 1277#[gpui::test]
 1278fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1279    init_test(cx, |_| {});
 1280
 1281    let view = cx.add_window(|cx| {
 1282        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1283        build_editor(buffer.clone(), cx)
 1284    });
 1285
 1286    assert_eq!('ⓐ'.len_utf8(), 3);
 1287    assert_eq!('α'.len_utf8(), 2);
 1288
 1289    _ = view.update(cx, |view, cx| {
 1290        view.fold_creases(
 1291            vec![
 1292                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1293                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1294                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1295            ],
 1296            true,
 1297            cx,
 1298        );
 1299        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1300
 1301        view.move_right(&MoveRight, cx);
 1302        assert_eq!(
 1303            view.selections.display_ranges(cx),
 1304            &[empty_range(0, "".len())]
 1305        );
 1306        view.move_right(&MoveRight, cx);
 1307        assert_eq!(
 1308            view.selections.display_ranges(cx),
 1309            &[empty_range(0, "ⓐⓑ".len())]
 1310        );
 1311        view.move_right(&MoveRight, cx);
 1312        assert_eq!(
 1313            view.selections.display_ranges(cx),
 1314            &[empty_range(0, "ⓐⓑ⋯".len())]
 1315        );
 1316
 1317        view.move_down(&MoveDown, cx);
 1318        assert_eq!(
 1319            view.selections.display_ranges(cx),
 1320            &[empty_range(1, "ab⋯e".len())]
 1321        );
 1322        view.move_left(&MoveLeft, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(1, "ab⋯".len())]
 1326        );
 1327        view.move_left(&MoveLeft, cx);
 1328        assert_eq!(
 1329            view.selections.display_ranges(cx),
 1330            &[empty_range(1, "ab".len())]
 1331        );
 1332        view.move_left(&MoveLeft, cx);
 1333        assert_eq!(
 1334            view.selections.display_ranges(cx),
 1335            &[empty_range(1, "a".len())]
 1336        );
 1337
 1338        view.move_down(&MoveDown, cx);
 1339        assert_eq!(
 1340            view.selections.display_ranges(cx),
 1341            &[empty_range(2, "α".len())]
 1342        );
 1343        view.move_right(&MoveRight, cx);
 1344        assert_eq!(
 1345            view.selections.display_ranges(cx),
 1346            &[empty_range(2, "αβ".len())]
 1347        );
 1348        view.move_right(&MoveRight, cx);
 1349        assert_eq!(
 1350            view.selections.display_ranges(cx),
 1351            &[empty_range(2, "αβ⋯".len())]
 1352        );
 1353        view.move_right(&MoveRight, cx);
 1354        assert_eq!(
 1355            view.selections.display_ranges(cx),
 1356            &[empty_range(2, "αβ⋯ε".len())]
 1357        );
 1358
 1359        view.move_up(&MoveUp, cx);
 1360        assert_eq!(
 1361            view.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯e".len())]
 1363        );
 1364        view.move_down(&MoveDown, cx);
 1365        assert_eq!(
 1366            view.selections.display_ranges(cx),
 1367            &[empty_range(2, "αβ⋯ε".len())]
 1368        );
 1369        view.move_up(&MoveUp, cx);
 1370        assert_eq!(
 1371            view.selections.display_ranges(cx),
 1372            &[empty_range(1, "ab⋯e".len())]
 1373        );
 1374
 1375        view.move_up(&MoveUp, cx);
 1376        assert_eq!(
 1377            view.selections.display_ranges(cx),
 1378            &[empty_range(0, "ⓐⓑ".len())]
 1379        );
 1380        view.move_left(&MoveLeft, cx);
 1381        assert_eq!(
 1382            view.selections.display_ranges(cx),
 1383            &[empty_range(0, "".len())]
 1384        );
 1385        view.move_left(&MoveLeft, cx);
 1386        assert_eq!(
 1387            view.selections.display_ranges(cx),
 1388            &[empty_range(0, "".len())]
 1389        );
 1390    });
 1391}
 1392
 1393#[gpui::test]
 1394fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1395    init_test(cx, |_| {});
 1396
 1397    let view = cx.add_window(|cx| {
 1398        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1399        build_editor(buffer.clone(), cx)
 1400    });
 1401    _ = view.update(cx, |view, cx| {
 1402        view.change_selections(None, cx, |s| {
 1403            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1404        });
 1405
 1406        // moving above start of document should move selection to start of document,
 1407        // but the next move down should still be at the original goal_x
 1408        view.move_up(&MoveUp, cx);
 1409        assert_eq!(
 1410            view.selections.display_ranges(cx),
 1411            &[empty_range(0, "".len())]
 1412        );
 1413
 1414        view.move_down(&MoveDown, cx);
 1415        assert_eq!(
 1416            view.selections.display_ranges(cx),
 1417            &[empty_range(1, "abcd".len())]
 1418        );
 1419
 1420        view.move_down(&MoveDown, cx);
 1421        assert_eq!(
 1422            view.selections.display_ranges(cx),
 1423            &[empty_range(2, "αβγ".len())]
 1424        );
 1425
 1426        view.move_down(&MoveDown, cx);
 1427        assert_eq!(
 1428            view.selections.display_ranges(cx),
 1429            &[empty_range(3, "abcd".len())]
 1430        );
 1431
 1432        view.move_down(&MoveDown, cx);
 1433        assert_eq!(
 1434            view.selections.display_ranges(cx),
 1435            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1436        );
 1437
 1438        // moving past end of document should not change goal_x
 1439        view.move_down(&MoveDown, cx);
 1440        assert_eq!(
 1441            view.selections.display_ranges(cx),
 1442            &[empty_range(5, "".len())]
 1443        );
 1444
 1445        view.move_down(&MoveDown, cx);
 1446        assert_eq!(
 1447            view.selections.display_ranges(cx),
 1448            &[empty_range(5, "".len())]
 1449        );
 1450
 1451        view.move_up(&MoveUp, cx);
 1452        assert_eq!(
 1453            view.selections.display_ranges(cx),
 1454            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1455        );
 1456
 1457        view.move_up(&MoveUp, cx);
 1458        assert_eq!(
 1459            view.selections.display_ranges(cx),
 1460            &[empty_range(3, "abcd".len())]
 1461        );
 1462
 1463        view.move_up(&MoveUp, cx);
 1464        assert_eq!(
 1465            view.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468    });
 1469}
 1470
 1471#[gpui::test]
 1472fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1473    init_test(cx, |_| {});
 1474    let move_to_beg = MoveToBeginningOfLine {
 1475        stop_at_soft_wraps: true,
 1476    };
 1477
 1478    let move_to_end = MoveToEndOfLine {
 1479        stop_at_soft_wraps: true,
 1480    };
 1481
 1482    let view = cx.add_window(|cx| {
 1483        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1484        build_editor(buffer, cx)
 1485    });
 1486    _ = view.update(cx, |view, cx| {
 1487        view.change_selections(None, cx, |s| {
 1488            s.select_display_ranges([
 1489                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1490                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1491            ]);
 1492        });
 1493    });
 1494
 1495    _ = view.update(cx, |view, cx| {
 1496        view.move_to_beginning_of_line(&move_to_beg, cx);
 1497        assert_eq!(
 1498            view.selections.display_ranges(cx),
 1499            &[
 1500                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1501                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1502            ]
 1503        );
 1504    });
 1505
 1506    _ = view.update(cx, |view, cx| {
 1507        view.move_to_beginning_of_line(&move_to_beg, cx);
 1508        assert_eq!(
 1509            view.selections.display_ranges(cx),
 1510            &[
 1511                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1512                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1513            ]
 1514        );
 1515    });
 1516
 1517    _ = view.update(cx, |view, cx| {
 1518        view.move_to_beginning_of_line(&move_to_beg, cx);
 1519        assert_eq!(
 1520            view.selections.display_ranges(cx),
 1521            &[
 1522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1523                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1524            ]
 1525        );
 1526    });
 1527
 1528    _ = view.update(cx, |view, cx| {
 1529        view.move_to_end_of_line(&move_to_end, cx);
 1530        assert_eq!(
 1531            view.selections.display_ranges(cx),
 1532            &[
 1533                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1534                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1535            ]
 1536        );
 1537    });
 1538
 1539    // Moving to the end of line again is a no-op.
 1540    _ = view.update(cx, |view, cx| {
 1541        view.move_to_end_of_line(&move_to_end, cx);
 1542        assert_eq!(
 1543            view.selections.display_ranges(cx),
 1544            &[
 1545                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1546                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1547            ]
 1548        );
 1549    });
 1550
 1551    _ = view.update(cx, |view, cx| {
 1552        view.move_left(&MoveLeft, cx);
 1553        view.select_to_beginning_of_line(
 1554            &SelectToBeginningOfLine {
 1555                stop_at_soft_wraps: true,
 1556            },
 1557            cx,
 1558        );
 1559        assert_eq!(
 1560            view.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = view.update(cx, |view, cx| {
 1569        view.select_to_beginning_of_line(
 1570            &SelectToBeginningOfLine {
 1571                stop_at_soft_wraps: true,
 1572            },
 1573            cx,
 1574        );
 1575        assert_eq!(
 1576            view.selections.display_ranges(cx),
 1577            &[
 1578                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1579                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1580            ]
 1581        );
 1582    });
 1583
 1584    _ = view.update(cx, |view, cx| {
 1585        view.select_to_beginning_of_line(
 1586            &SelectToBeginningOfLine {
 1587                stop_at_soft_wraps: true,
 1588            },
 1589            cx,
 1590        );
 1591        assert_eq!(
 1592            view.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1595                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = view.update(cx, |view, cx| {
 1601        view.select_to_end_of_line(
 1602            &SelectToEndOfLine {
 1603                stop_at_soft_wraps: true,
 1604            },
 1605            cx,
 1606        );
 1607        assert_eq!(
 1608            view.selections.display_ranges(cx),
 1609            &[
 1610                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1611                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1612            ]
 1613        );
 1614    });
 1615
 1616    _ = view.update(cx, |view, cx| {
 1617        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1618        assert_eq!(view.display_text(cx), "ab\n  de");
 1619        assert_eq!(
 1620            view.selections.display_ranges(cx),
 1621            &[
 1622                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1623                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1624            ]
 1625        );
 1626    });
 1627
 1628    _ = view.update(cx, |view, cx| {
 1629        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1630        assert_eq!(view.display_text(cx), "\n");
 1631        assert_eq!(
 1632            view.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1636            ]
 1637        );
 1638    });
 1639}
 1640
 1641#[gpui::test]
 1642fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1643    init_test(cx, |_| {});
 1644    let move_to_beg = MoveToBeginningOfLine {
 1645        stop_at_soft_wraps: false,
 1646    };
 1647
 1648    let move_to_end = MoveToEndOfLine {
 1649        stop_at_soft_wraps: false,
 1650    };
 1651
 1652    let view = cx.add_window(|cx| {
 1653        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1654        build_editor(buffer, cx)
 1655    });
 1656
 1657    _ = view.update(cx, |view, cx| {
 1658        view.set_wrap_width(Some(140.0.into()), cx);
 1659
 1660        // We expect the following lines after wrapping
 1661        // ```
 1662        // thequickbrownfox
 1663        // jumpedoverthelazydo
 1664        // gs
 1665        // ```
 1666        // The final `gs` was soft-wrapped onto a new line.
 1667        assert_eq!(
 1668            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1669            view.display_text(cx),
 1670        );
 1671
 1672        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1673        // Start the cursor at the `k` on the first line
 1674        view.change_selections(None, cx, |s| {
 1675            s.select_display_ranges([
 1676                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1677            ]);
 1678        });
 1679
 1680        // Moving to the beginning of the line should put us at the beginning of the line.
 1681        view.move_to_beginning_of_line(&move_to_beg, cx);
 1682        assert_eq!(
 1683            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1684            view.selections.display_ranges(cx)
 1685        );
 1686
 1687        // Moving to the end of the line should put us at the end of the line.
 1688        view.move_to_end_of_line(&move_to_end, cx);
 1689        assert_eq!(
 1690            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1691            view.selections.display_ranges(cx)
 1692        );
 1693
 1694        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1695        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1696        view.change_selections(None, cx, |s| {
 1697            s.select_display_ranges([
 1698                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1699            ]);
 1700        });
 1701
 1702        // Moving to the beginning of the line should put us at the start of the second line of
 1703        // display text, i.e., the `j`.
 1704        view.move_to_beginning_of_line(&move_to_beg, cx);
 1705        assert_eq!(
 1706            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1707            view.selections.display_ranges(cx)
 1708        );
 1709
 1710        // Moving to the beginning of the line again should be a no-op.
 1711        view.move_to_beginning_of_line(&move_to_beg, cx);
 1712        assert_eq!(
 1713            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1714            view.selections.display_ranges(cx)
 1715        );
 1716
 1717        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1718        // next display line.
 1719        view.move_to_end_of_line(&move_to_end, cx);
 1720        assert_eq!(
 1721            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1722            view.selections.display_ranges(cx)
 1723        );
 1724
 1725        // Moving to the end of the line again should be a no-op.
 1726        view.move_to_end_of_line(&move_to_end, cx);
 1727        assert_eq!(
 1728            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1729            view.selections.display_ranges(cx)
 1730        );
 1731    });
 1732}
 1733
 1734#[gpui::test]
 1735fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1736    init_test(cx, |_| {});
 1737
 1738    let view = cx.add_window(|cx| {
 1739        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1740        build_editor(buffer, cx)
 1741    });
 1742    _ = view.update(cx, |view, cx| {
 1743        view.change_selections(None, cx, |s| {
 1744            s.select_display_ranges([
 1745                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1746                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1747            ])
 1748        });
 1749
 1750        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1751        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1752
 1753        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1754        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1755
 1756        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1757        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1758
 1759        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1760        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1761
 1762        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1763        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1764
 1765        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1766        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1767
 1768        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1769        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1770
 1771        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1772        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1773
 1774        view.move_right(&MoveRight, cx);
 1775        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1776        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1777
 1778        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1779        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1780
 1781        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1782        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1783    });
 1784}
 1785
 1786#[gpui::test]
 1787fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1788    init_test(cx, |_| {});
 1789
 1790    let view = cx.add_window(|cx| {
 1791        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1792        build_editor(buffer, cx)
 1793    });
 1794
 1795    _ = view.update(cx, |view, cx| {
 1796        view.set_wrap_width(Some(140.0.into()), cx);
 1797        assert_eq!(
 1798            view.display_text(cx),
 1799            "use one::{\n    two::three::\n    four::five\n};"
 1800        );
 1801
 1802        view.change_selections(None, cx, |s| {
 1803            s.select_display_ranges([
 1804                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1805            ]);
 1806        });
 1807
 1808        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1809        assert_eq!(
 1810            view.selections.display_ranges(cx),
 1811            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1812        );
 1813
 1814        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1815        assert_eq!(
 1816            view.selections.display_ranges(cx),
 1817            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1818        );
 1819
 1820        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1821        assert_eq!(
 1822            view.selections.display_ranges(cx),
 1823            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1824        );
 1825
 1826        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1827        assert_eq!(
 1828            view.selections.display_ranges(cx),
 1829            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1830        );
 1831
 1832        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1833        assert_eq!(
 1834            view.selections.display_ranges(cx),
 1835            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1836        );
 1837
 1838        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1839        assert_eq!(
 1840            view.selections.display_ranges(cx),
 1841            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1842        );
 1843    });
 1844}
 1845
 1846#[gpui::test]
 1847async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1848    init_test(cx, |_| {});
 1849    let mut cx = EditorTestContext::new(cx).await;
 1850
 1851    let line_height = cx.editor(|editor, cx| {
 1852        editor
 1853            .style()
 1854            .unwrap()
 1855            .text
 1856            .line_height_in_pixels(cx.rem_size())
 1857    });
 1858    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1859
 1860    cx.set_state(
 1861        &r#"ˇone
 1862        two
 1863
 1864        three
 1865        fourˇ
 1866        five
 1867
 1868        six"#
 1869            .unindent(),
 1870    );
 1871
 1872    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1873    cx.assert_editor_state(
 1874        &r#"one
 1875        two
 1876        ˇ
 1877        three
 1878        four
 1879        five
 1880        ˇ
 1881        six"#
 1882            .unindent(),
 1883    );
 1884
 1885    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1886    cx.assert_editor_state(
 1887        &r#"one
 1888        two
 1889
 1890        three
 1891        four
 1892        five
 1893        ˇ
 1894        sixˇ"#
 1895            .unindent(),
 1896    );
 1897
 1898    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1899    cx.assert_editor_state(
 1900        &r#"one
 1901        two
 1902
 1903        three
 1904        four
 1905        five
 1906
 1907        sixˇ"#
 1908            .unindent(),
 1909    );
 1910
 1911    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1912    cx.assert_editor_state(
 1913        &r#"one
 1914        two
 1915
 1916        three
 1917        four
 1918        five
 1919        ˇ
 1920        six"#
 1921            .unindent(),
 1922    );
 1923
 1924    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1925    cx.assert_editor_state(
 1926        &r#"one
 1927        two
 1928        ˇ
 1929        three
 1930        four
 1931        five
 1932
 1933        six"#
 1934            .unindent(),
 1935    );
 1936
 1937    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1938    cx.assert_editor_state(
 1939        &r#"ˇone
 1940        two
 1941
 1942        three
 1943        four
 1944        five
 1945
 1946        six"#
 1947            .unindent(),
 1948    );
 1949}
 1950
 1951#[gpui::test]
 1952async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1953    init_test(cx, |_| {});
 1954    let mut cx = EditorTestContext::new(cx).await;
 1955    let line_height = cx.editor(|editor, cx| {
 1956        editor
 1957            .style()
 1958            .unwrap()
 1959            .text
 1960            .line_height_in_pixels(cx.rem_size())
 1961    });
 1962    let window = cx.window;
 1963    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1964
 1965    cx.set_state(
 1966        r#"ˇone
 1967        two
 1968        three
 1969        four
 1970        five
 1971        six
 1972        seven
 1973        eight
 1974        nine
 1975        ten
 1976        "#,
 1977    );
 1978
 1979    cx.update_editor(|editor, cx| {
 1980        assert_eq!(
 1981            editor.snapshot(cx).scroll_position(),
 1982            gpui::Point::new(0., 0.)
 1983        );
 1984        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1985        assert_eq!(
 1986            editor.snapshot(cx).scroll_position(),
 1987            gpui::Point::new(0., 3.)
 1988        );
 1989        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1990        assert_eq!(
 1991            editor.snapshot(cx).scroll_position(),
 1992            gpui::Point::new(0., 6.)
 1993        );
 1994        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1995        assert_eq!(
 1996            editor.snapshot(cx).scroll_position(),
 1997            gpui::Point::new(0., 3.)
 1998        );
 1999
 2000        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 2001        assert_eq!(
 2002            editor.snapshot(cx).scroll_position(),
 2003            gpui::Point::new(0., 1.)
 2004        );
 2005        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2006        assert_eq!(
 2007            editor.snapshot(cx).scroll_position(),
 2008            gpui::Point::new(0., 3.)
 2009        );
 2010    });
 2011}
 2012
 2013#[gpui::test]
 2014async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2015    init_test(cx, |_| {});
 2016    let mut cx = EditorTestContext::new(cx).await;
 2017
 2018    let line_height = cx.update_editor(|editor, cx| {
 2019        editor.set_vertical_scroll_margin(2, cx);
 2020        editor
 2021            .style()
 2022            .unwrap()
 2023            .text
 2024            .line_height_in_pixels(cx.rem_size())
 2025    });
 2026    let window = cx.window;
 2027    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2028
 2029    cx.set_state(
 2030        r#"ˇone
 2031            two
 2032            three
 2033            four
 2034            five
 2035            six
 2036            seven
 2037            eight
 2038            nine
 2039            ten
 2040        "#,
 2041    );
 2042    cx.update_editor(|editor, cx| {
 2043        assert_eq!(
 2044            editor.snapshot(cx).scroll_position(),
 2045            gpui::Point::new(0., 0.0)
 2046        );
 2047    });
 2048
 2049    // Add a cursor below the visible area. Since both cursors cannot fit
 2050    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2051    // allows the vertical scroll margin below that cursor.
 2052    cx.update_editor(|editor, cx| {
 2053        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2054            selections.select_ranges([
 2055                Point::new(0, 0)..Point::new(0, 0),
 2056                Point::new(6, 0)..Point::new(6, 0),
 2057            ]);
 2058        })
 2059    });
 2060    cx.update_editor(|editor, cx| {
 2061        assert_eq!(
 2062            editor.snapshot(cx).scroll_position(),
 2063            gpui::Point::new(0., 3.0)
 2064        );
 2065    });
 2066
 2067    // Move down. The editor cursor scrolls down to track the newest cursor.
 2068    cx.update_editor(|editor, cx| {
 2069        editor.move_down(&Default::default(), cx);
 2070    });
 2071    cx.update_editor(|editor, cx| {
 2072        assert_eq!(
 2073            editor.snapshot(cx).scroll_position(),
 2074            gpui::Point::new(0., 4.0)
 2075        );
 2076    });
 2077
 2078    // Add a cursor above the visible area. Since both cursors fit on screen,
 2079    // the editor scrolls to show both.
 2080    cx.update_editor(|editor, cx| {
 2081        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2082            selections.select_ranges([
 2083                Point::new(1, 0)..Point::new(1, 0),
 2084                Point::new(6, 0)..Point::new(6, 0),
 2085            ]);
 2086        })
 2087    });
 2088    cx.update_editor(|editor, cx| {
 2089        assert_eq!(
 2090            editor.snapshot(cx).scroll_position(),
 2091            gpui::Point::new(0., 1.0)
 2092        );
 2093    });
 2094}
 2095
 2096#[gpui::test]
 2097async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2098    init_test(cx, |_| {});
 2099    let mut cx = EditorTestContext::new(cx).await;
 2100
 2101    let line_height = cx.editor(|editor, cx| {
 2102        editor
 2103            .style()
 2104            .unwrap()
 2105            .text
 2106            .line_height_in_pixels(cx.rem_size())
 2107    });
 2108    let window = cx.window;
 2109    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2110    cx.set_state(
 2111        &r#"
 2112        ˇone
 2113        two
 2114        threeˇ
 2115        four
 2116        five
 2117        six
 2118        seven
 2119        eight
 2120        nine
 2121        ten
 2122        "#
 2123        .unindent(),
 2124    );
 2125
 2126    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2127    cx.assert_editor_state(
 2128        &r#"
 2129        one
 2130        two
 2131        three
 2132        ˇfour
 2133        five
 2134        sixˇ
 2135        seven
 2136        eight
 2137        nine
 2138        ten
 2139        "#
 2140        .unindent(),
 2141    );
 2142
 2143    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2144    cx.assert_editor_state(
 2145        &r#"
 2146        one
 2147        two
 2148        three
 2149        four
 2150        five
 2151        six
 2152        ˇseven
 2153        eight
 2154        nineˇ
 2155        ten
 2156        "#
 2157        .unindent(),
 2158    );
 2159
 2160    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2161    cx.assert_editor_state(
 2162        &r#"
 2163        one
 2164        two
 2165        three
 2166        ˇfour
 2167        five
 2168        sixˇ
 2169        seven
 2170        eight
 2171        nine
 2172        ten
 2173        "#
 2174        .unindent(),
 2175    );
 2176
 2177    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2178    cx.assert_editor_state(
 2179        &r#"
 2180        ˇone
 2181        two
 2182        threeˇ
 2183        four
 2184        five
 2185        six
 2186        seven
 2187        eight
 2188        nine
 2189        ten
 2190        "#
 2191        .unindent(),
 2192    );
 2193
 2194    // Test select collapsing
 2195    cx.update_editor(|editor, cx| {
 2196        editor.move_page_down(&MovePageDown::default(), cx);
 2197        editor.move_page_down(&MovePageDown::default(), cx);
 2198        editor.move_page_down(&MovePageDown::default(), cx);
 2199    });
 2200    cx.assert_editor_state(
 2201        &r#"
 2202        one
 2203        two
 2204        three
 2205        four
 2206        five
 2207        six
 2208        seven
 2209        eight
 2210        nine
 2211        ˇten
 2212        ˇ"#
 2213        .unindent(),
 2214    );
 2215}
 2216
 2217#[gpui::test]
 2218async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2219    init_test(cx, |_| {});
 2220    let mut cx = EditorTestContext::new(cx).await;
 2221    cx.set_state("one «two threeˇ» four");
 2222    cx.update_editor(|editor, cx| {
 2223        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2224        assert_eq!(editor.text(cx), " four");
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2230    init_test(cx, |_| {});
 2231
 2232    let view = cx.add_window(|cx| {
 2233        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2234        build_editor(buffer.clone(), cx)
 2235    });
 2236
 2237    _ = view.update(cx, |view, cx| {
 2238        view.change_selections(None, cx, |s| {
 2239            s.select_display_ranges([
 2240                // an empty selection - the preceding word fragment is deleted
 2241                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2242                // characters selected - they are deleted
 2243                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2244            ])
 2245        });
 2246        view.delete_to_previous_word_start(
 2247            &DeleteToPreviousWordStart {
 2248                ignore_newlines: false,
 2249            },
 2250            cx,
 2251        );
 2252        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2253    });
 2254
 2255    _ = view.update(cx, |view, cx| {
 2256        view.change_selections(None, cx, |s| {
 2257            s.select_display_ranges([
 2258                // an empty selection - the following word fragment is deleted
 2259                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2260                // characters selected - they are deleted
 2261                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2262            ])
 2263        });
 2264        view.delete_to_next_word_end(
 2265            &DeleteToNextWordEnd {
 2266                ignore_newlines: false,
 2267            },
 2268            cx,
 2269        );
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2286        ignore_newlines: true,
 2287    };
 2288
 2289    _ = view.update(cx, |view, cx| {
 2290        view.change_selections(None, cx, |s| {
 2291            s.select_display_ranges([
 2292                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2293            ])
 2294        });
 2295        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2296        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2297        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2298        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2299        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2300        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2301        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2302        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2303        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2304        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2305        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2307    });
 2308}
 2309
 2310#[gpui::test]
 2311fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2312    init_test(cx, |_| {});
 2313
 2314    let view = cx.add_window(|cx| {
 2315        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2316        build_editor(buffer.clone(), cx)
 2317    });
 2318    let del_to_next_word_end = DeleteToNextWordEnd {
 2319        ignore_newlines: false,
 2320    };
 2321    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2322        ignore_newlines: true,
 2323    };
 2324
 2325    _ = view.update(cx, |view, cx| {
 2326        view.change_selections(None, cx, |s| {
 2327            s.select_display_ranges([
 2328                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2329            ])
 2330        });
 2331        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2332        assert_eq!(
 2333            view.buffer.read(cx).read(cx).text(),
 2334            "one\n   two\nthree\n   four"
 2335        );
 2336        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2337        assert_eq!(
 2338            view.buffer.read(cx).read(cx).text(),
 2339            "\n   two\nthree\n   four"
 2340        );
 2341        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2342        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2343        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2344        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2345        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2346        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2347        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2348        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2349    });
 2350}
 2351
 2352#[gpui::test]
 2353fn test_newline(cx: &mut TestAppContext) {
 2354    init_test(cx, |_| {});
 2355
 2356    let view = cx.add_window(|cx| {
 2357        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2358        build_editor(buffer.clone(), cx)
 2359    });
 2360
 2361    _ = view.update(cx, |view, cx| {
 2362        view.change_selections(None, cx, |s| {
 2363            s.select_display_ranges([
 2364                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2365                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2366                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2367            ])
 2368        });
 2369
 2370        view.newline(&Newline, cx);
 2371        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2372    });
 2373}
 2374
 2375#[gpui::test]
 2376fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2377    init_test(cx, |_| {});
 2378
 2379    let editor = cx.add_window(|cx| {
 2380        let buffer = MultiBuffer::build_simple(
 2381            "
 2382                a
 2383                b(
 2384                    X
 2385                )
 2386                c(
 2387                    X
 2388                )
 2389            "
 2390            .unindent()
 2391            .as_str(),
 2392            cx,
 2393        );
 2394        let mut editor = build_editor(buffer.clone(), cx);
 2395        editor.change_selections(None, cx, |s| {
 2396            s.select_ranges([
 2397                Point::new(2, 4)..Point::new(2, 5),
 2398                Point::new(5, 4)..Point::new(5, 5),
 2399            ])
 2400        });
 2401        editor
 2402    });
 2403
 2404    _ = editor.update(cx, |editor, cx| {
 2405        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2406        editor.buffer.update(cx, |buffer, cx| {
 2407            buffer.edit(
 2408                [
 2409                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2410                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2411                ],
 2412                None,
 2413                cx,
 2414            );
 2415            assert_eq!(
 2416                buffer.read(cx).text(),
 2417                "
 2418                    a
 2419                    b()
 2420                    c()
 2421                "
 2422                .unindent()
 2423            );
 2424        });
 2425        assert_eq!(
 2426            editor.selections.ranges(cx),
 2427            &[
 2428                Point::new(1, 2)..Point::new(1, 2),
 2429                Point::new(2, 2)..Point::new(2, 2),
 2430            ],
 2431        );
 2432
 2433        editor.newline(&Newline, cx);
 2434        assert_eq!(
 2435            editor.text(cx),
 2436            "
 2437                a
 2438                b(
 2439                )
 2440                c(
 2441                )
 2442            "
 2443            .unindent()
 2444        );
 2445
 2446        // The selections are moved after the inserted newlines
 2447        assert_eq!(
 2448            editor.selections.ranges(cx),
 2449            &[
 2450                Point::new(2, 0)..Point::new(2, 0),
 2451                Point::new(4, 0)..Point::new(4, 0),
 2452            ],
 2453        );
 2454    });
 2455}
 2456
 2457#[gpui::test]
 2458async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2459    init_test(cx, |settings| {
 2460        settings.defaults.tab_size = NonZeroU32::new(4)
 2461    });
 2462
 2463    let language = Arc::new(
 2464        Language::new(
 2465            LanguageConfig::default(),
 2466            Some(tree_sitter_rust::LANGUAGE.into()),
 2467        )
 2468        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2469        .unwrap(),
 2470    );
 2471
 2472    let mut cx = EditorTestContext::new(cx).await;
 2473    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2474    cx.set_state(indoc! {"
 2475        const a: ˇA = (
 2476 2477                «const_functionˇ»(ˇ),
 2478                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2479 2480        ˇ);ˇ
 2481    "});
 2482
 2483    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2484    cx.assert_editor_state(indoc! {"
 2485        ˇ
 2486        const a: A = (
 2487            ˇ
 2488            (
 2489                ˇ
 2490                ˇ
 2491                const_function(),
 2492                ˇ
 2493                ˇ
 2494                ˇ
 2495                ˇ
 2496                something_else,
 2497                ˇ
 2498            )
 2499            ˇ
 2500            ˇ
 2501        );
 2502    "});
 2503}
 2504
 2505#[gpui::test]
 2506async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2507    init_test(cx, |settings| {
 2508        settings.defaults.tab_size = NonZeroU32::new(4)
 2509    });
 2510
 2511    let language = Arc::new(
 2512        Language::new(
 2513            LanguageConfig::default(),
 2514            Some(tree_sitter_rust::LANGUAGE.into()),
 2515        )
 2516        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2517        .unwrap(),
 2518    );
 2519
 2520    let mut cx = EditorTestContext::new(cx).await;
 2521    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2522    cx.set_state(indoc! {"
 2523        const a: ˇA = (
 2524 2525                «const_functionˇ»(ˇ),
 2526                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2527 2528        ˇ);ˇ
 2529    "});
 2530
 2531    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2532    cx.assert_editor_state(indoc! {"
 2533        const a: A = (
 2534            ˇ
 2535            (
 2536                ˇ
 2537                const_function(),
 2538                ˇ
 2539                ˇ
 2540                something_else,
 2541                ˇ
 2542                ˇ
 2543                ˇ
 2544                ˇ
 2545            )
 2546            ˇ
 2547        );
 2548        ˇ
 2549        ˇ
 2550    "});
 2551}
 2552
 2553#[gpui::test]
 2554async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2555    init_test(cx, |settings| {
 2556        settings.defaults.tab_size = NonZeroU32::new(4)
 2557    });
 2558
 2559    let language = Arc::new(Language::new(
 2560        LanguageConfig {
 2561            line_comments: vec!["//".into()],
 2562            ..LanguageConfig::default()
 2563        },
 2564        None,
 2565    ));
 2566    {
 2567        let mut cx = EditorTestContext::new(cx).await;
 2568        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2569        cx.set_state(indoc! {"
 2570        // Fooˇ
 2571    "});
 2572
 2573        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2574        cx.assert_editor_state(indoc! {"
 2575        // Foo
 2576        //ˇ
 2577    "});
 2578        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2579        cx.set_state(indoc! {"
 2580        ˇ// Foo
 2581    "});
 2582        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2583        cx.assert_editor_state(indoc! {"
 2584
 2585        ˇ// Foo
 2586    "});
 2587    }
 2588    // Ensure that comment continuations can be disabled.
 2589    update_test_language_settings(cx, |settings| {
 2590        settings.defaults.extend_comment_on_newline = Some(false);
 2591    });
 2592    let mut cx = EditorTestContext::new(cx).await;
 2593    cx.set_state(indoc! {"
 2594        // Fooˇ
 2595    "});
 2596    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2597    cx.assert_editor_state(indoc! {"
 2598        // Foo
 2599        ˇ
 2600    "});
 2601}
 2602
 2603#[gpui::test]
 2604fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2605    init_test(cx, |_| {});
 2606
 2607    let editor = cx.add_window(|cx| {
 2608        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2609        let mut editor = build_editor(buffer.clone(), cx);
 2610        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2611        editor
 2612    });
 2613
 2614    _ = editor.update(cx, |editor, cx| {
 2615        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2616        editor.buffer.update(cx, |buffer, cx| {
 2617            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2618            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2619        });
 2620        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2621
 2622        editor.insert("Z", cx);
 2623        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2624
 2625        // The selections are moved after the inserted characters
 2626        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2627    });
 2628}
 2629
 2630#[gpui::test]
 2631async fn test_tab(cx: &mut gpui::TestAppContext) {
 2632    init_test(cx, |settings| {
 2633        settings.defaults.tab_size = NonZeroU32::new(3)
 2634    });
 2635
 2636    let mut cx = EditorTestContext::new(cx).await;
 2637    cx.set_state(indoc! {"
 2638        ˇabˇc
 2639        ˇ🏀ˇ🏀ˇefg
 2640 2641    "});
 2642    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2643    cx.assert_editor_state(indoc! {"
 2644           ˇab ˇc
 2645           ˇ🏀  ˇ🏀  ˇefg
 2646        d  ˇ
 2647    "});
 2648
 2649    cx.set_state(indoc! {"
 2650        a
 2651        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655        a
 2656           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2657    "});
 2658}
 2659
 2660#[gpui::test]
 2661async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2662    init_test(cx, |_| {});
 2663
 2664    let mut cx = EditorTestContext::new(cx).await;
 2665    let language = Arc::new(
 2666        Language::new(
 2667            LanguageConfig::default(),
 2668            Some(tree_sitter_rust::LANGUAGE.into()),
 2669        )
 2670        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2671        .unwrap(),
 2672    );
 2673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2674
 2675    // cursors that are already at the suggested indent level insert
 2676    // a soft tab. cursors that are to the left of the suggested indent
 2677    // auto-indent their line.
 2678    cx.set_state(indoc! {"
 2679        ˇ
 2680        const a: B = (
 2681            c(
 2682                d(
 2683        ˇ
 2684                )
 2685        ˇ
 2686        ˇ    )
 2687        );
 2688    "});
 2689    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2690    cx.assert_editor_state(indoc! {"
 2691            ˇ
 2692        const a: B = (
 2693            c(
 2694                d(
 2695                    ˇ
 2696                )
 2697                ˇ
 2698            ˇ)
 2699        );
 2700    "});
 2701
 2702    // handle auto-indent when there are multiple cursors on the same line
 2703    cx.set_state(indoc! {"
 2704        const a: B = (
 2705            c(
 2706        ˇ    ˇ
 2707        ˇ    )
 2708        );
 2709    "});
 2710    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2711    cx.assert_editor_state(indoc! {"
 2712        const a: B = (
 2713            c(
 2714                ˇ
 2715            ˇ)
 2716        );
 2717    "});
 2718}
 2719
 2720#[gpui::test]
 2721async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2722    init_test(cx, |settings| {
 2723        settings.defaults.tab_size = NonZeroU32::new(4)
 2724    });
 2725
 2726    let language = Arc::new(
 2727        Language::new(
 2728            LanguageConfig::default(),
 2729            Some(tree_sitter_rust::LANGUAGE.into()),
 2730        )
 2731        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2732        .unwrap(),
 2733    );
 2734
 2735    let mut cx = EditorTestContext::new(cx).await;
 2736    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2737    cx.set_state(indoc! {"
 2738        fn a() {
 2739            if b {
 2740        \t ˇc
 2741            }
 2742        }
 2743    "});
 2744
 2745    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2746    cx.assert_editor_state(indoc! {"
 2747        fn a() {
 2748            if b {
 2749                ˇc
 2750            }
 2751        }
 2752    "});
 2753}
 2754
 2755#[gpui::test]
 2756async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2757    init_test(cx, |settings| {
 2758        settings.defaults.tab_size = NonZeroU32::new(4);
 2759    });
 2760
 2761    let mut cx = EditorTestContext::new(cx).await;
 2762
 2763    cx.set_state(indoc! {"
 2764          «oneˇ» «twoˇ»
 2765        three
 2766         four
 2767    "});
 2768    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770            «oneˇ» «twoˇ»
 2771        three
 2772         four
 2773    "});
 2774
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        «oneˇ» «twoˇ»
 2778        three
 2779         four
 2780    "});
 2781
 2782    // select across line ending
 2783    cx.set_state(indoc! {"
 2784        one two
 2785        t«hree
 2786        ˇ» four
 2787    "});
 2788    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        one two
 2791            t«hree
 2792        ˇ» four
 2793    "});
 2794
 2795    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2796    cx.assert_editor_state(indoc! {"
 2797        one two
 2798        t«hree
 2799        ˇ» four
 2800    "});
 2801
 2802    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2803    cx.set_state(indoc! {"
 2804        one two
 2805        ˇthree
 2806            four
 2807    "});
 2808    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2809    cx.assert_editor_state(indoc! {"
 2810        one two
 2811            ˇthree
 2812            four
 2813    "});
 2814
 2815    cx.set_state(indoc! {"
 2816        one two
 2817        ˇ    three
 2818            four
 2819    "});
 2820    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2821    cx.assert_editor_state(indoc! {"
 2822        one two
 2823        ˇthree
 2824            four
 2825    "});
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.hard_tabs = Some(true);
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835
 2836    // select two ranges on one line
 2837    cx.set_state(indoc! {"
 2838        «oneˇ» «twoˇ»
 2839        three
 2840        four
 2841    "});
 2842    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844        \t«oneˇ» «twoˇ»
 2845        three
 2846        four
 2847    "});
 2848    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2849    cx.assert_editor_state(indoc! {"
 2850        \t\t«oneˇ» «twoˇ»
 2851        three
 2852        four
 2853    "});
 2854    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2855    cx.assert_editor_state(indoc! {"
 2856        \t«oneˇ» «twoˇ»
 2857        three
 2858        four
 2859    "});
 2860    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2861    cx.assert_editor_state(indoc! {"
 2862        «oneˇ» «twoˇ»
 2863        three
 2864        four
 2865    "});
 2866
 2867    // select across a line ending
 2868    cx.set_state(indoc! {"
 2869        one two
 2870        t«hree
 2871        ˇ»four
 2872    "});
 2873    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2874    cx.assert_editor_state(indoc! {"
 2875        one two
 2876        \tt«hree
 2877        ˇ»four
 2878    "});
 2879    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2880    cx.assert_editor_state(indoc! {"
 2881        one two
 2882        \t\tt«hree
 2883        ˇ»four
 2884    "});
 2885    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2886    cx.assert_editor_state(indoc! {"
 2887        one two
 2888        \tt«hree
 2889        ˇ»four
 2890    "});
 2891    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2892    cx.assert_editor_state(indoc! {"
 2893        one two
 2894        t«hree
 2895        ˇ»four
 2896    "});
 2897
 2898    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2899    cx.set_state(indoc! {"
 2900        one two
 2901        ˇthree
 2902        four
 2903    "});
 2904    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        one two
 2907        ˇthree
 2908        four
 2909    "});
 2910    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        one two
 2913        \tˇthree
 2914        four
 2915    "});
 2916    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2917    cx.assert_editor_state(indoc! {"
 2918        one two
 2919        ˇthree
 2920        four
 2921    "});
 2922}
 2923
 2924#[gpui::test]
 2925fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2926    init_test(cx, |settings| {
 2927        settings.languages.extend([
 2928            (
 2929                "TOML".into(),
 2930                LanguageSettingsContent {
 2931                    tab_size: NonZeroU32::new(2),
 2932                    ..Default::default()
 2933                },
 2934            ),
 2935            (
 2936                "Rust".into(),
 2937                LanguageSettingsContent {
 2938                    tab_size: NonZeroU32::new(4),
 2939                    ..Default::default()
 2940                },
 2941            ),
 2942        ]);
 2943    });
 2944
 2945    let toml_language = Arc::new(Language::new(
 2946        LanguageConfig {
 2947            name: "TOML".into(),
 2948            ..Default::default()
 2949        },
 2950        None,
 2951    ));
 2952    let rust_language = Arc::new(Language::new(
 2953        LanguageConfig {
 2954            name: "Rust".into(),
 2955            ..Default::default()
 2956        },
 2957        None,
 2958    ));
 2959
 2960    let toml_buffer =
 2961        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2962    let rust_buffer = cx.new_model(|cx| {
 2963        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2964    });
 2965    let multibuffer = cx.new_model(|cx| {
 2966        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2967        multibuffer.push_excerpts(
 2968            toml_buffer.clone(),
 2969            [ExcerptRange {
 2970                context: Point::new(0, 0)..Point::new(2, 0),
 2971                primary: None,
 2972            }],
 2973            cx,
 2974        );
 2975        multibuffer.push_excerpts(
 2976            rust_buffer.clone(),
 2977            [ExcerptRange {
 2978                context: Point::new(0, 0)..Point::new(1, 0),
 2979                primary: None,
 2980            }],
 2981            cx,
 2982        );
 2983        multibuffer
 2984    });
 2985
 2986    cx.add_window(|cx| {
 2987        let mut editor = build_editor(multibuffer, cx);
 2988
 2989        assert_eq!(
 2990            editor.text(cx),
 2991            indoc! {"
 2992                a = 1
 2993                b = 2
 2994
 2995                const c: usize = 3;
 2996            "}
 2997        );
 2998
 2999        select_ranges(
 3000            &mut editor,
 3001            indoc! {"
 3002                «aˇ» = 1
 3003                b = 2
 3004
 3005                «const c:ˇ» usize = 3;
 3006            "},
 3007            cx,
 3008        );
 3009
 3010        editor.tab(&Tab, cx);
 3011        assert_text_with_selections(
 3012            &mut editor,
 3013            indoc! {"
 3014                  «aˇ» = 1
 3015                b = 2
 3016
 3017                    «const c:ˇ» usize = 3;
 3018            "},
 3019            cx,
 3020        );
 3021        editor.tab_prev(&TabPrev, cx);
 3022        assert_text_with_selections(
 3023            &mut editor,
 3024            indoc! {"
 3025                «aˇ» = 1
 3026                b = 2
 3027
 3028                «const c:ˇ» usize = 3;
 3029            "},
 3030            cx,
 3031        );
 3032
 3033        editor
 3034    });
 3035}
 3036
 3037#[gpui::test]
 3038async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3039    init_test(cx, |_| {});
 3040
 3041    let mut cx = EditorTestContext::new(cx).await;
 3042
 3043    // Basic backspace
 3044    cx.set_state(indoc! {"
 3045        onˇe two three
 3046        fou«rˇ» five six
 3047        seven «ˇeight nine
 3048        »ten
 3049    "});
 3050    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        oˇe two three
 3053        fouˇ five six
 3054        seven ˇten
 3055    "});
 3056
 3057    // Test backspace inside and around indents
 3058    cx.set_state(indoc! {"
 3059        zero
 3060            ˇone
 3061                ˇtwo
 3062            ˇ ˇ ˇ  three
 3063        ˇ  ˇ  four
 3064    "});
 3065    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        zero
 3068        ˇone
 3069            ˇtwo
 3070        ˇ  threeˇ  four
 3071    "});
 3072
 3073    // Test backspace with line_mode set to true
 3074    cx.update_editor(|e, _| e.selections.line_mode = true);
 3075    cx.set_state(indoc! {"
 3076        The ˇquick ˇbrown
 3077        fox jumps over
 3078        the lazy dog
 3079        ˇThe qu«ick bˇ»rown"});
 3080    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3081    cx.assert_editor_state(indoc! {"
 3082        ˇfox jumps over
 3083        the lazy dogˇ"});
 3084}
 3085
 3086#[gpui::test]
 3087async fn test_delete(cx: &mut gpui::TestAppContext) {
 3088    init_test(cx, |_| {});
 3089
 3090    let mut cx = EditorTestContext::new(cx).await;
 3091    cx.set_state(indoc! {"
 3092        onˇe two three
 3093        fou«rˇ» five six
 3094        seven «ˇeight nine
 3095        »ten
 3096    "});
 3097    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        onˇ two three
 3100        fouˇ five six
 3101        seven ˇten
 3102    "});
 3103
 3104    // Test backspace with line_mode set to true
 3105    cx.update_editor(|e, _| e.selections.line_mode = true);
 3106    cx.set_state(indoc! {"
 3107        The ˇquick ˇbrown
 3108        fox «ˇjum»ps over
 3109        the lazy dog
 3110        ˇThe qu«ick bˇ»rown"});
 3111    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3112    cx.assert_editor_state("ˇthe lazy dogˇ");
 3113}
 3114
 3115#[gpui::test]
 3116fn test_delete_line(cx: &mut TestAppContext) {
 3117    init_test(cx, |_| {});
 3118
 3119    let view = cx.add_window(|cx| {
 3120        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3121        build_editor(buffer, cx)
 3122    });
 3123    _ = view.update(cx, |view, cx| {
 3124        view.change_selections(None, cx, |s| {
 3125            s.select_display_ranges([
 3126                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3127                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3128                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3129            ])
 3130        });
 3131        view.delete_line(&DeleteLine, cx);
 3132        assert_eq!(view.display_text(cx), "ghi");
 3133        assert_eq!(
 3134            view.selections.display_ranges(cx),
 3135            vec![
 3136                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3137                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3138            ]
 3139        );
 3140    });
 3141
 3142    let view = cx.add_window(|cx| {
 3143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3144        build_editor(buffer, cx)
 3145    });
 3146    _ = view.update(cx, |view, cx| {
 3147        view.change_selections(None, cx, |s| {
 3148            s.select_display_ranges([
 3149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3150            ])
 3151        });
 3152        view.delete_line(&DeleteLine, cx);
 3153        assert_eq!(view.display_text(cx), "ghi\n");
 3154        assert_eq!(
 3155            view.selections.display_ranges(cx),
 3156            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3157        );
 3158    });
 3159}
 3160
 3161#[gpui::test]
 3162fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3163    init_test(cx, |_| {});
 3164
 3165    cx.add_window(|cx| {
 3166        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3167        let mut editor = build_editor(buffer.clone(), cx);
 3168        let buffer = buffer.read(cx).as_singleton().unwrap();
 3169
 3170        assert_eq!(
 3171            editor.selections.ranges::<Point>(cx),
 3172            &[Point::new(0, 0)..Point::new(0, 0)]
 3173        );
 3174
 3175        // When on single line, replace newline at end by space
 3176        editor.join_lines(&JoinLines, cx);
 3177        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3178        assert_eq!(
 3179            editor.selections.ranges::<Point>(cx),
 3180            &[Point::new(0, 3)..Point::new(0, 3)]
 3181        );
 3182
 3183        // When multiple lines are selected, remove newlines that are spanned by the selection
 3184        editor.change_selections(None, cx, |s| {
 3185            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3186        });
 3187        editor.join_lines(&JoinLines, cx);
 3188        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3189        assert_eq!(
 3190            editor.selections.ranges::<Point>(cx),
 3191            &[Point::new(0, 11)..Point::new(0, 11)]
 3192        );
 3193
 3194        // Undo should be transactional
 3195        editor.undo(&Undo, cx);
 3196        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3197        assert_eq!(
 3198            editor.selections.ranges::<Point>(cx),
 3199            &[Point::new(0, 5)..Point::new(2, 2)]
 3200        );
 3201
 3202        // When joining an empty line don't insert a space
 3203        editor.change_selections(None, cx, |s| {
 3204            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3205        });
 3206        editor.join_lines(&JoinLines, cx);
 3207        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3208        assert_eq!(
 3209            editor.selections.ranges::<Point>(cx),
 3210            [Point::new(2, 3)..Point::new(2, 3)]
 3211        );
 3212
 3213        // We can remove trailing newlines
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3216        assert_eq!(
 3217            editor.selections.ranges::<Point>(cx),
 3218            [Point::new(2, 3)..Point::new(2, 3)]
 3219        );
 3220
 3221        // We don't blow up on the last line
 3222        editor.join_lines(&JoinLines, cx);
 3223        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3224        assert_eq!(
 3225            editor.selections.ranges::<Point>(cx),
 3226            [Point::new(2, 3)..Point::new(2, 3)]
 3227        );
 3228
 3229        // reset to test indentation
 3230        editor.buffer.update(cx, |buffer, cx| {
 3231            buffer.edit(
 3232                [
 3233                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3234                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3235                ],
 3236                None,
 3237                cx,
 3238            )
 3239        });
 3240
 3241        // We remove any leading spaces
 3242        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3243        editor.change_selections(None, cx, |s| {
 3244            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3245        });
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3248
 3249        // We don't insert a space for a line containing only spaces
 3250        editor.join_lines(&JoinLines, cx);
 3251        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3252
 3253        // We ignore any leading tabs
 3254        editor.join_lines(&JoinLines, cx);
 3255        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3256
 3257        editor
 3258    });
 3259}
 3260
 3261#[gpui::test]
 3262fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3263    init_test(cx, |_| {});
 3264
 3265    cx.add_window(|cx| {
 3266        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3267        let mut editor = build_editor(buffer.clone(), cx);
 3268        let buffer = buffer.read(cx).as_singleton().unwrap();
 3269
 3270        editor.change_selections(None, cx, |s| {
 3271            s.select_ranges([
 3272                Point::new(0, 2)..Point::new(1, 1),
 3273                Point::new(1, 2)..Point::new(1, 2),
 3274                Point::new(3, 1)..Point::new(3, 2),
 3275            ])
 3276        });
 3277
 3278        editor.join_lines(&JoinLines, cx);
 3279        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3280
 3281        assert_eq!(
 3282            editor.selections.ranges::<Point>(cx),
 3283            [
 3284                Point::new(0, 7)..Point::new(0, 7),
 3285                Point::new(1, 3)..Point::new(1, 3)
 3286            ]
 3287        );
 3288        editor
 3289    });
 3290}
 3291
 3292#[gpui::test]
 3293async fn test_join_lines_with_git_diff_base(
 3294    executor: BackgroundExecutor,
 3295    cx: &mut gpui::TestAppContext,
 3296) {
 3297    init_test(cx, |_| {});
 3298
 3299    let mut cx = EditorTestContext::new(cx).await;
 3300
 3301    let diff_base = r#"
 3302        Line 0
 3303        Line 1
 3304        Line 2
 3305        Line 3
 3306        "#
 3307    .unindent();
 3308
 3309    cx.set_state(
 3310        &r#"
 3311        ˇLine 0
 3312        Line 1
 3313        Line 2
 3314        Line 3
 3315        "#
 3316        .unindent(),
 3317    );
 3318
 3319    cx.set_diff_base(&diff_base);
 3320    executor.run_until_parked();
 3321
 3322    // Join lines
 3323    cx.update_editor(|editor, cx| {
 3324        editor.join_lines(&JoinLines, cx);
 3325    });
 3326    executor.run_until_parked();
 3327
 3328    cx.assert_editor_state(
 3329        &r#"
 3330        Line 0ˇ Line 1
 3331        Line 2
 3332        Line 3
 3333        "#
 3334        .unindent(),
 3335    );
 3336    // Join again
 3337    cx.update_editor(|editor, cx| {
 3338        editor.join_lines(&JoinLines, cx);
 3339    });
 3340    executor.run_until_parked();
 3341
 3342    cx.assert_editor_state(
 3343        &r#"
 3344        Line 0 Line 1ˇ Line 2
 3345        Line 3
 3346        "#
 3347        .unindent(),
 3348    );
 3349}
 3350
 3351#[gpui::test]
 3352async fn test_custom_newlines_cause_no_false_positive_diffs(
 3353    executor: BackgroundExecutor,
 3354    cx: &mut gpui::TestAppContext,
 3355) {
 3356    init_test(cx, |_| {});
 3357    let mut cx = EditorTestContext::new(cx).await;
 3358    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3359    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3360    executor.run_until_parked();
 3361
 3362    cx.update_editor(|editor, cx| {
 3363        let snapshot = editor.snapshot(cx);
 3364        assert_eq!(
 3365            snapshot
 3366                .diff_map
 3367                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
 3368                .collect::<Vec<_>>(),
 3369            Vec::new(),
 3370            "Should not have any diffs for files with custom newlines"
 3371        );
 3372    });
 3373}
 3374
 3375#[gpui::test]
 3376async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3377    init_test(cx, |_| {});
 3378
 3379    let mut cx = EditorTestContext::new(cx).await;
 3380
 3381    // Test sort_lines_case_insensitive()
 3382    cx.set_state(indoc! {"
 3383        «z
 3384        y
 3385        x
 3386        Z
 3387        Y
 3388        Xˇ»
 3389    "});
 3390    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3391    cx.assert_editor_state(indoc! {"
 3392        «x
 3393        X
 3394        y
 3395        Y
 3396        z
 3397        Zˇ»
 3398    "});
 3399
 3400    // Test reverse_lines()
 3401    cx.set_state(indoc! {"
 3402        «5
 3403        4
 3404        3
 3405        2
 3406        1ˇ»
 3407    "});
 3408    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3409    cx.assert_editor_state(indoc! {"
 3410        «1
 3411        2
 3412        3
 3413        4
 3414        5ˇ»
 3415    "});
 3416
 3417    // Skip testing shuffle_line()
 3418
 3419    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3420    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3421
 3422    // Don't manipulate when cursor is on single line, but expand the selection
 3423    cx.set_state(indoc! {"
 3424        ddˇdd
 3425        ccc
 3426        bb
 3427        a
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «ddddˇ»
 3432        ccc
 3433        bb
 3434        a
 3435    "});
 3436
 3437    // Basic manipulate case
 3438    // Start selection moves to column 0
 3439    // End of selection shrinks to fit shorter line
 3440    cx.set_state(indoc! {"
 3441        dd«d
 3442        ccc
 3443        bb
 3444        aaaaaˇ»
 3445    "});
 3446    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3447    cx.assert_editor_state(indoc! {"
 3448        «aaaaa
 3449        bb
 3450        ccc
 3451        dddˇ»
 3452    "});
 3453
 3454    // Manipulate case with newlines
 3455    cx.set_state(indoc! {"
 3456        dd«d
 3457        ccc
 3458
 3459        bb
 3460        aaaaa
 3461
 3462        ˇ»
 3463    "});
 3464    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3465    cx.assert_editor_state(indoc! {"
 3466        «
 3467
 3468        aaaaa
 3469        bb
 3470        ccc
 3471        dddˇ»
 3472
 3473    "});
 3474
 3475    // Adding new line
 3476    cx.set_state(indoc! {"
 3477        aa«a
 3478        bbˇ»b
 3479    "});
 3480    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3481    cx.assert_editor_state(indoc! {"
 3482        «aaa
 3483        bbb
 3484        added_lineˇ»
 3485    "});
 3486
 3487    // Removing line
 3488    cx.set_state(indoc! {"
 3489        aa«a
 3490        bbbˇ»
 3491    "});
 3492    cx.update_editor(|e, cx| {
 3493        e.manipulate_lines(cx, |lines| {
 3494            lines.pop();
 3495        })
 3496    });
 3497    cx.assert_editor_state(indoc! {"
 3498        «aaaˇ»
 3499    "});
 3500
 3501    // Removing all lines
 3502    cx.set_state(indoc! {"
 3503        aa«a
 3504        bbbˇ»
 3505    "});
 3506    cx.update_editor(|e, cx| {
 3507        e.manipulate_lines(cx, |lines| {
 3508            lines.drain(..);
 3509        })
 3510    });
 3511    cx.assert_editor_state(indoc! {"
 3512        ˇ
 3513    "});
 3514}
 3515
 3516#[gpui::test]
 3517async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3518    init_test(cx, |_| {});
 3519
 3520    let mut cx = EditorTestContext::new(cx).await;
 3521
 3522    // Consider continuous selection as single selection
 3523    cx.set_state(indoc! {"
 3524        Aaa«aa
 3525        cˇ»c«c
 3526        bb
 3527        aaaˇ»aa
 3528    "});
 3529    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3530    cx.assert_editor_state(indoc! {"
 3531        «Aaaaa
 3532        ccc
 3533        bb
 3534        aaaaaˇ»
 3535    "});
 3536
 3537    cx.set_state(indoc! {"
 3538        Aaa«aa
 3539        cˇ»c«c
 3540        bb
 3541        aaaˇ»aa
 3542    "});
 3543    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3544    cx.assert_editor_state(indoc! {"
 3545        «Aaaaa
 3546        ccc
 3547        bbˇ»
 3548    "});
 3549
 3550    // Consider non continuous selection as distinct dedup operations
 3551    cx.set_state(indoc! {"
 3552        «aaaaa
 3553        bb
 3554        aaaaa
 3555        aaaaaˇ»
 3556
 3557        aaa«aaˇ»
 3558    "});
 3559    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3560    cx.assert_editor_state(indoc! {"
 3561        «aaaaa
 3562        bbˇ»
 3563
 3564        «aaaaaˇ»
 3565    "});
 3566}
 3567
 3568#[gpui::test]
 3569async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3570    init_test(cx, |_| {});
 3571
 3572    let mut cx = EditorTestContext::new(cx).await;
 3573
 3574    cx.set_state(indoc! {"
 3575        «Aaa
 3576        aAa
 3577        Aaaˇ»
 3578    "});
 3579    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «Aaa
 3582        aAaˇ»
 3583    "});
 3584
 3585    cx.set_state(indoc! {"
 3586        «Aaa
 3587        aAa
 3588        aaAˇ»
 3589    "});
 3590    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3591    cx.assert_editor_state(indoc! {"
 3592        «Aaaˇ»
 3593    "});
 3594}
 3595
 3596#[gpui::test]
 3597async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3598    init_test(cx, |_| {});
 3599
 3600    let mut cx = EditorTestContext::new(cx).await;
 3601
 3602    // Manipulate with multiple selections on a single line
 3603    cx.set_state(indoc! {"
 3604        dd«dd
 3605        cˇ»c«c
 3606        bb
 3607        aaaˇ»aa
 3608    "});
 3609    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3610    cx.assert_editor_state(indoc! {"
 3611        «aaaaa
 3612        bb
 3613        ccc
 3614        ddddˇ»
 3615    "});
 3616
 3617    // Manipulate with multiple disjoin selections
 3618    cx.set_state(indoc! {"
 3619 3620        4
 3621        3
 3622        2
 3623        1ˇ»
 3624
 3625        dd«dd
 3626        ccc
 3627        bb
 3628        aaaˇ»aa
 3629    "});
 3630    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3631    cx.assert_editor_state(indoc! {"
 3632        «1
 3633        2
 3634        3
 3635        4
 3636        5ˇ»
 3637
 3638        «aaaaa
 3639        bb
 3640        ccc
 3641        ddddˇ»
 3642    "});
 3643
 3644    // Adding lines on each selection
 3645    cx.set_state(indoc! {"
 3646 3647        1ˇ»
 3648
 3649        bb«bb
 3650        aaaˇ»aa
 3651    "});
 3652    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3653    cx.assert_editor_state(indoc! {"
 3654        «2
 3655        1
 3656        added lineˇ»
 3657
 3658        «bbbb
 3659        aaaaa
 3660        added lineˇ»
 3661    "});
 3662
 3663    // Removing lines on each selection
 3664    cx.set_state(indoc! {"
 3665 3666        1ˇ»
 3667
 3668        bb«bb
 3669        aaaˇ»aa
 3670    "});
 3671    cx.update_editor(|e, cx| {
 3672        e.manipulate_lines(cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «2ˇ»
 3678
 3679        «bbbbˇ»
 3680    "});
 3681}
 3682
 3683#[gpui::test]
 3684async fn test_manipulate_text(cx: &mut TestAppContext) {
 3685    init_test(cx, |_| {});
 3686
 3687    let mut cx = EditorTestContext::new(cx).await;
 3688
 3689    // Test convert_to_upper_case()
 3690    cx.set_state(indoc! {"
 3691        «hello worldˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «HELLO WORLDˇ»
 3696    "});
 3697
 3698    // Test convert_to_lower_case()
 3699    cx.set_state(indoc! {"
 3700        «HELLO WORLDˇ»
 3701    "});
 3702    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3703    cx.assert_editor_state(indoc! {"
 3704        «hello worldˇ»
 3705    "});
 3706
 3707    // Test multiple line, single selection case
 3708    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3709    cx.set_state(indoc! {"
 3710        «The quick brown
 3711        fox jumps over
 3712        the lazy dogˇ»
 3713    "});
 3714    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3715    cx.assert_editor_state(indoc! {"
 3716        «The Quick Brown
 3717        Fox Jumps Over
 3718        The Lazy Dogˇ»
 3719    "});
 3720
 3721    // Test multiple line, single selection case
 3722    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3723    cx.set_state(indoc! {"
 3724        «The quick brown
 3725        fox jumps over
 3726        the lazy dogˇ»
 3727    "});
 3728    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3729    cx.assert_editor_state(indoc! {"
 3730        «TheQuickBrown
 3731        FoxJumpsOver
 3732        TheLazyDogˇ»
 3733    "});
 3734
 3735    // From here on out, test more complex cases of manipulate_text()
 3736
 3737    // Test no selection case - should affect words cursors are in
 3738    // Cursor at beginning, middle, and end of word
 3739    cx.set_state(indoc! {"
 3740        ˇhello big beauˇtiful worldˇ
 3741    "});
 3742    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3743    cx.assert_editor_state(indoc! {"
 3744        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3745    "});
 3746
 3747    // Test multiple selections on a single line and across multiple lines
 3748    cx.set_state(indoc! {"
 3749        «Theˇ» quick «brown
 3750        foxˇ» jumps «overˇ»
 3751        the «lazyˇ» dog
 3752    "});
 3753    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3754    cx.assert_editor_state(indoc! {"
 3755        «THEˇ» quick «BROWN
 3756        FOXˇ» jumps «OVERˇ»
 3757        the «LAZYˇ» dog
 3758    "});
 3759
 3760    // Test case where text length grows
 3761    cx.set_state(indoc! {"
 3762        «tschüߡ»
 3763    "});
 3764    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3765    cx.assert_editor_state(indoc! {"
 3766        «TSCHÜSSˇ»
 3767    "});
 3768
 3769    // Test to make sure we don't crash when text shrinks
 3770    cx.set_state(indoc! {"
 3771        aaa_bbbˇ
 3772    "});
 3773    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3774    cx.assert_editor_state(indoc! {"
 3775        «aaaBbbˇ»
 3776    "});
 3777
 3778    // Test to make sure we all aware of the fact that each word can grow and shrink
 3779    // Final selections should be aware of this fact
 3780    cx.set_state(indoc! {"
 3781        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3782    "});
 3783    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3784    cx.assert_editor_state(indoc! {"
 3785        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3786    "});
 3787
 3788    cx.set_state(indoc! {"
 3789        «hElLo, WoRld!ˇ»
 3790    "});
 3791    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3792    cx.assert_editor_state(indoc! {"
 3793        «HeLlO, wOrLD!ˇ»
 3794    "});
 3795}
 3796
 3797#[gpui::test]
 3798fn test_duplicate_line(cx: &mut TestAppContext) {
 3799    init_test(cx, |_| {});
 3800
 3801    let view = cx.add_window(|cx| {
 3802        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3803        build_editor(buffer, cx)
 3804    });
 3805    _ = view.update(cx, |view, cx| {
 3806        view.change_selections(None, cx, |s| {
 3807            s.select_display_ranges([
 3808                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3809                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3810                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3811                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3812            ])
 3813        });
 3814        view.duplicate_line_down(&DuplicateLineDown, cx);
 3815        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3816        assert_eq!(
 3817            view.selections.display_ranges(cx),
 3818            vec![
 3819                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3820                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3821                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3822                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3823            ]
 3824        );
 3825    });
 3826
 3827    let view = cx.add_window(|cx| {
 3828        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3829        build_editor(buffer, cx)
 3830    });
 3831    _ = view.update(cx, |view, cx| {
 3832        view.change_selections(None, cx, |s| {
 3833            s.select_display_ranges([
 3834                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3835                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3836            ])
 3837        });
 3838        view.duplicate_line_down(&DuplicateLineDown, cx);
 3839        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3840        assert_eq!(
 3841            view.selections.display_ranges(cx),
 3842            vec![
 3843                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3844                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3845            ]
 3846        );
 3847    });
 3848
 3849    // With `move_upwards` the selections stay in place, except for
 3850    // the lines inserted above them
 3851    let view = cx.add_window(|cx| {
 3852        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3853        build_editor(buffer, cx)
 3854    });
 3855    _ = view.update(cx, |view, cx| {
 3856        view.change_selections(None, cx, |s| {
 3857            s.select_display_ranges([
 3858                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3859                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3860                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3861                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3862            ])
 3863        });
 3864        view.duplicate_line_up(&DuplicateLineUp, cx);
 3865        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3866        assert_eq!(
 3867            view.selections.display_ranges(cx),
 3868            vec![
 3869                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3870                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3871                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3872                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3873            ]
 3874        );
 3875    });
 3876
 3877    let view = cx.add_window(|cx| {
 3878        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3879        build_editor(buffer, cx)
 3880    });
 3881    _ = view.update(cx, |view, cx| {
 3882        view.change_selections(None, cx, |s| {
 3883            s.select_display_ranges([
 3884                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3885                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3886            ])
 3887        });
 3888        view.duplicate_line_up(&DuplicateLineUp, cx);
 3889        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3890        assert_eq!(
 3891            view.selections.display_ranges(cx),
 3892            vec![
 3893                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3894                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3895            ]
 3896        );
 3897    });
 3898
 3899    let view = cx.add_window(|cx| {
 3900        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3901        build_editor(buffer, cx)
 3902    });
 3903    _ = view.update(cx, |view, cx| {
 3904        view.change_selections(None, cx, |s| {
 3905            s.select_display_ranges([
 3906                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3907                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3908            ])
 3909        });
 3910        view.duplicate_selection(&DuplicateSelection, cx);
 3911        assert_eq!(view.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 3912        assert_eq!(
 3913            view.selections.display_ranges(cx),
 3914            vec![
 3915                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3916                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 3917            ]
 3918        );
 3919    });
 3920}
 3921
 3922#[gpui::test]
 3923fn test_move_line_up_down(cx: &mut TestAppContext) {
 3924    init_test(cx, |_| {});
 3925
 3926    let view = cx.add_window(|cx| {
 3927        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3928        build_editor(buffer, cx)
 3929    });
 3930    _ = view.update(cx, |view, cx| {
 3931        view.fold_creases(
 3932            vec![
 3933                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3934                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3935                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3936            ],
 3937            true,
 3938            cx,
 3939        );
 3940        view.change_selections(None, cx, |s| {
 3941            s.select_display_ranges([
 3942                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3943                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3944                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3945                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3946            ])
 3947        });
 3948        assert_eq!(
 3949            view.display_text(cx),
 3950            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3951        );
 3952
 3953        view.move_line_up(&MoveLineUp, cx);
 3954        assert_eq!(
 3955            view.display_text(cx),
 3956            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3957        );
 3958        assert_eq!(
 3959            view.selections.display_ranges(cx),
 3960            vec![
 3961                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3962                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3963                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3964                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3965            ]
 3966        );
 3967    });
 3968
 3969    _ = view.update(cx, |view, cx| {
 3970        view.move_line_down(&MoveLineDown, cx);
 3971        assert_eq!(
 3972            view.display_text(cx),
 3973            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3974        );
 3975        assert_eq!(
 3976            view.selections.display_ranges(cx),
 3977            vec![
 3978                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3979                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3980                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3981                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3982            ]
 3983        );
 3984    });
 3985
 3986    _ = view.update(cx, |view, cx| {
 3987        view.move_line_down(&MoveLineDown, cx);
 3988        assert_eq!(
 3989            view.display_text(cx),
 3990            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3991        );
 3992        assert_eq!(
 3993            view.selections.display_ranges(cx),
 3994            vec![
 3995                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3996                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3997                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3998                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3999            ]
 4000        );
 4001    });
 4002
 4003    _ = view.update(cx, |view, cx| {
 4004        view.move_line_up(&MoveLineUp, cx);
 4005        assert_eq!(
 4006            view.display_text(cx),
 4007            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4008        );
 4009        assert_eq!(
 4010            view.selections.display_ranges(cx),
 4011            vec![
 4012                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4013                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4014                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4015                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4016            ]
 4017        );
 4018    });
 4019}
 4020
 4021#[gpui::test]
 4022fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4023    init_test(cx, |_| {});
 4024
 4025    let editor = cx.add_window(|cx| {
 4026        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4027        build_editor(buffer, cx)
 4028    });
 4029    _ = editor.update(cx, |editor, cx| {
 4030        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4031        editor.insert_blocks(
 4032            [BlockProperties {
 4033                style: BlockStyle::Fixed,
 4034                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4035                height: 1,
 4036                render: Arc::new(|_| div().into_any()),
 4037                priority: 0,
 4038            }],
 4039            Some(Autoscroll::fit()),
 4040            cx,
 4041        );
 4042        editor.change_selections(None, cx, |s| {
 4043            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4044        });
 4045        editor.move_line_down(&MoveLineDown, cx);
 4046    });
 4047}
 4048
 4049#[gpui::test]
 4050async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4051    init_test(cx, |_| {});
 4052
 4053    let mut cx = EditorTestContext::new(cx).await;
 4054    cx.set_state(
 4055        &"
 4056            ˇzero
 4057            one
 4058            two
 4059            three
 4060            four
 4061            five
 4062        "
 4063        .unindent(),
 4064    );
 4065
 4066    // Create a four-line block that replaces three lines of text.
 4067    cx.update_editor(|editor, cx| {
 4068        let snapshot = editor.snapshot(cx);
 4069        let snapshot = &snapshot.buffer_snapshot;
 4070        let placement = BlockPlacement::Replace(
 4071            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4072        );
 4073        editor.insert_blocks(
 4074            [BlockProperties {
 4075                placement,
 4076                height: 4,
 4077                style: BlockStyle::Sticky,
 4078                render: Arc::new(|_| gpui::div().into_any_element()),
 4079                priority: 0,
 4080            }],
 4081            None,
 4082            cx,
 4083        );
 4084    });
 4085
 4086    // Move down so that the cursor touches the block.
 4087    cx.update_editor(|editor, cx| {
 4088        editor.move_down(&Default::default(), cx);
 4089    });
 4090    cx.assert_editor_state(
 4091        &"
 4092            zero
 4093            «one
 4094            two
 4095            threeˇ»
 4096            four
 4097            five
 4098        "
 4099        .unindent(),
 4100    );
 4101
 4102    // Move down past the block.
 4103    cx.update_editor(|editor, cx| {
 4104        editor.move_down(&Default::default(), cx);
 4105    });
 4106    cx.assert_editor_state(
 4107        &"
 4108            zero
 4109            one
 4110            two
 4111            three
 4112            ˇfour
 4113            five
 4114        "
 4115        .unindent(),
 4116    );
 4117}
 4118
 4119#[gpui::test]
 4120fn test_transpose(cx: &mut TestAppContext) {
 4121    init_test(cx, |_| {});
 4122
 4123    _ = cx.add_window(|cx| {
 4124        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4125        editor.set_style(EditorStyle::default(), cx);
 4126        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4127        editor.transpose(&Default::default(), cx);
 4128        assert_eq!(editor.text(cx), "bac");
 4129        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4130
 4131        editor.transpose(&Default::default(), cx);
 4132        assert_eq!(editor.text(cx), "bca");
 4133        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4134
 4135        editor.transpose(&Default::default(), cx);
 4136        assert_eq!(editor.text(cx), "bac");
 4137        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4138
 4139        editor
 4140    });
 4141
 4142    _ = cx.add_window(|cx| {
 4143        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4144        editor.set_style(EditorStyle::default(), cx);
 4145        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4146        editor.transpose(&Default::default(), cx);
 4147        assert_eq!(editor.text(cx), "acb\nde");
 4148        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4149
 4150        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4151        editor.transpose(&Default::default(), cx);
 4152        assert_eq!(editor.text(cx), "acbd\ne");
 4153        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4154
 4155        editor.transpose(&Default::default(), cx);
 4156        assert_eq!(editor.text(cx), "acbde\n");
 4157        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4158
 4159        editor.transpose(&Default::default(), cx);
 4160        assert_eq!(editor.text(cx), "acbd\ne");
 4161        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4162
 4163        editor
 4164    });
 4165
 4166    _ = cx.add_window(|cx| {
 4167        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4168        editor.set_style(EditorStyle::default(), cx);
 4169        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4170        editor.transpose(&Default::default(), cx);
 4171        assert_eq!(editor.text(cx), "bacd\ne");
 4172        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4173
 4174        editor.transpose(&Default::default(), cx);
 4175        assert_eq!(editor.text(cx), "bcade\n");
 4176        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4177
 4178        editor.transpose(&Default::default(), cx);
 4179        assert_eq!(editor.text(cx), "bcda\ne");
 4180        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4181
 4182        editor.transpose(&Default::default(), cx);
 4183        assert_eq!(editor.text(cx), "bcade\n");
 4184        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4185
 4186        editor.transpose(&Default::default(), cx);
 4187        assert_eq!(editor.text(cx), "bcaed\n");
 4188        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4189
 4190        editor
 4191    });
 4192
 4193    _ = cx.add_window(|cx| {
 4194        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4195        editor.set_style(EditorStyle::default(), cx);
 4196        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4197        editor.transpose(&Default::default(), cx);
 4198        assert_eq!(editor.text(cx), "🏀🍐✋");
 4199        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4200
 4201        editor.transpose(&Default::default(), cx);
 4202        assert_eq!(editor.text(cx), "🏀✋🍐");
 4203        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4204
 4205        editor.transpose(&Default::default(), cx);
 4206        assert_eq!(editor.text(cx), "🏀🍐✋");
 4207        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4208
 4209        editor
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214async fn test_rewrap(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let mut cx = EditorTestContext::new(cx).await;
 4218
 4219    let language_with_c_comments = Arc::new(Language::new(
 4220        LanguageConfig {
 4221            line_comments: vec!["// ".into()],
 4222            ..LanguageConfig::default()
 4223        },
 4224        None,
 4225    ));
 4226    let language_with_pound_comments = Arc::new(Language::new(
 4227        LanguageConfig {
 4228            line_comments: vec!["# ".into()],
 4229            ..LanguageConfig::default()
 4230        },
 4231        None,
 4232    ));
 4233    let markdown_language = Arc::new(Language::new(
 4234        LanguageConfig {
 4235            name: "Markdown".into(),
 4236            ..LanguageConfig::default()
 4237        },
 4238        None,
 4239    ));
 4240    let language_with_doc_comments = Arc::new(Language::new(
 4241        LanguageConfig {
 4242            line_comments: vec!["// ".into(), "/// ".into()],
 4243            ..LanguageConfig::default()
 4244        },
 4245        Some(tree_sitter_rust::LANGUAGE.into()),
 4246    ));
 4247
 4248    let plaintext_language = Arc::new(Language::new(
 4249        LanguageConfig {
 4250            name: "Plain Text".into(),
 4251            ..LanguageConfig::default()
 4252        },
 4253        None,
 4254    ));
 4255
 4256    assert_rewrap(
 4257        indoc! {"
 4258            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4259        "},
 4260        indoc! {"
 4261            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4262            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4263            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4264            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4265            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4266            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4267            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4268            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4269            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4270            // porttitor id. Aliquam id accumsan eros.
 4271        "},
 4272        language_with_c_comments.clone(),
 4273        &mut cx,
 4274    );
 4275
 4276    // Test that rewrapping works inside of a selection
 4277    assert_rewrap(
 4278        indoc! {"
 4279            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4280        "},
 4281        indoc! {"
 4282            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4283            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4284            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4285            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4286            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4287            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4288            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4289            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4290            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4291            // porttitor id. Aliquam id accumsan eros.ˇ»
 4292        "},
 4293        language_with_c_comments.clone(),
 4294        &mut cx,
 4295    );
 4296
 4297    // Test that cursors that expand to the same region are collapsed.
 4298    assert_rewrap(
 4299        indoc! {"
 4300            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4301            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4302            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4303            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4304        "},
 4305        indoc! {"
 4306            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4307            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4308            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4309            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4310            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4311            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4312            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4313            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4314            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4315            // porttitor id. Aliquam id accumsan eros.
 4316        "},
 4317        language_with_c_comments.clone(),
 4318        &mut cx,
 4319    );
 4320
 4321    // Test that non-contiguous selections are treated separately.
 4322    assert_rewrap(
 4323        indoc! {"
 4324            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4325            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4326            //
 4327            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4328            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4329        "},
 4330        indoc! {"
 4331            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4332            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4333            // auctor, eu lacinia sapien scelerisque.
 4334            //
 4335            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4336            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4337            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4338            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4339            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4340            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4341            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4342        "},
 4343        language_with_c_comments.clone(),
 4344        &mut cx,
 4345    );
 4346
 4347    // Test that different comment prefixes are supported.
 4348    assert_rewrap(
 4349        indoc! {"
 4350            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4351        "},
 4352        indoc! {"
 4353            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4354            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4355            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4356            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4357            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4358            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4359            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4360            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4361            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4362            # accumsan eros.
 4363        "},
 4364        language_with_pound_comments.clone(),
 4365        &mut cx,
 4366    );
 4367
 4368    // Test that rewrapping is ignored outside of comments in most languages.
 4369    assert_rewrap(
 4370        indoc! {"
 4371            /// Adds two numbers.
 4372            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4373            fn add(a: u32, b: u32) -> u32 {
 4374                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4375            }
 4376        "},
 4377        indoc! {"
 4378            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4379            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4380            fn add(a: u32, b: u32) -> u32 {
 4381                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4382            }
 4383        "},
 4384        language_with_doc_comments.clone(),
 4385        &mut cx,
 4386    );
 4387
 4388    // Test that rewrapping works in Markdown and Plain Text languages.
 4389    assert_rewrap(
 4390        indoc! {"
 4391            # Hello
 4392
 4393            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4394        "},
 4395        indoc! {"
 4396            # Hello
 4397
 4398            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4399            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4400            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4401            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4402            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4403            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4404            Integer sit amet scelerisque nisi.
 4405        "},
 4406        markdown_language,
 4407        &mut cx,
 4408    );
 4409
 4410    assert_rewrap(
 4411        indoc! {"
 4412            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4413        "},
 4414        indoc! {"
 4415            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4416            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4417            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4418            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4419            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4420            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4421            Integer sit amet scelerisque nisi.
 4422        "},
 4423        plaintext_language,
 4424        &mut cx,
 4425    );
 4426
 4427    // Test rewrapping unaligned comments in a selection.
 4428    assert_rewrap(
 4429        indoc! {"
 4430            fn foo() {
 4431                if true {
 4432            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4433            // Praesent semper egestas tellus id dignissim.ˇ»
 4434                    do_something();
 4435                } else {
 4436                    //
 4437                }
 4438            }
 4439        "},
 4440        indoc! {"
 4441            fn foo() {
 4442                if true {
 4443            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4444                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4445                    // egestas tellus id dignissim.ˇ»
 4446                    do_something();
 4447                } else {
 4448                    //
 4449                }
 4450            }
 4451        "},
 4452        language_with_doc_comments.clone(),
 4453        &mut cx,
 4454    );
 4455
 4456    assert_rewrap(
 4457        indoc! {"
 4458            fn foo() {
 4459                if true {
 4460            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4461            // Praesent semper egestas tellus id dignissim.»
 4462                    do_something();
 4463                } else {
 4464                    //
 4465                }
 4466
 4467            }
 4468        "},
 4469        indoc! {"
 4470            fn foo() {
 4471                if true {
 4472            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4473                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4474                    // egestas tellus id dignissim.»
 4475                    do_something();
 4476                } else {
 4477                    //
 4478                }
 4479
 4480            }
 4481        "},
 4482        language_with_doc_comments.clone(),
 4483        &mut cx,
 4484    );
 4485
 4486    #[track_caller]
 4487    fn assert_rewrap(
 4488        unwrapped_text: &str,
 4489        wrapped_text: &str,
 4490        language: Arc<Language>,
 4491        cx: &mut EditorTestContext,
 4492    ) {
 4493        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4494        cx.set_state(unwrapped_text);
 4495        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4496        cx.assert_editor_state(wrapped_text);
 4497    }
 4498}
 4499
 4500#[gpui::test]
 4501async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4502    init_test(cx, |_| {});
 4503
 4504    let mut cx = EditorTestContext::new(cx).await;
 4505
 4506    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4507    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4508    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4509
 4510    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4511    cx.set_state("two ˇfour ˇsix ˇ");
 4512    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4513    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4514
 4515    // Paste again but with only two cursors. Since the number of cursors doesn't
 4516    // match the number of slices in the clipboard, the entire clipboard text
 4517    // is pasted at each cursor.
 4518    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4519    cx.update_editor(|e, cx| {
 4520        e.handle_input("( ", cx);
 4521        e.paste(&Paste, cx);
 4522        e.handle_input(") ", cx);
 4523    });
 4524    cx.assert_editor_state(
 4525        &([
 4526            "( one✅ ",
 4527            "three ",
 4528            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4529            "three ",
 4530            "five ) ˇ",
 4531        ]
 4532        .join("\n")),
 4533    );
 4534
 4535    // Cut with three selections, one of which is full-line.
 4536    cx.set_state(indoc! {"
 4537        1«2ˇ»3
 4538        4ˇ567
 4539        «8ˇ»9"});
 4540    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4541    cx.assert_editor_state(indoc! {"
 4542        1ˇ3
 4543        ˇ9"});
 4544
 4545    // Paste with three selections, noticing how the copied selection that was full-line
 4546    // gets inserted before the second cursor.
 4547    cx.set_state(indoc! {"
 4548        1ˇ3
 4549 4550        «oˇ»ne"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        12ˇ3
 4554        4567
 4555 4556        8ˇne"});
 4557
 4558    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4559    cx.set_state(indoc! {"
 4560        The quick brown
 4561        fox juˇmps over
 4562        the lazy dog"});
 4563    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4564    assert_eq!(
 4565        cx.read_from_clipboard()
 4566            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4567        Some("fox jumps over\n".to_string())
 4568    );
 4569
 4570    // Paste with three selections, noticing how the copied full-line selection is inserted
 4571    // before the empty selections but replaces the selection that is non-empty.
 4572    cx.set_state(indoc! {"
 4573        Tˇhe quick brown
 4574        «foˇ»x jumps over
 4575        tˇhe lazy dog"});
 4576    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4577    cx.assert_editor_state(indoc! {"
 4578        fox jumps over
 4579        Tˇhe quick brown
 4580        fox jumps over
 4581        ˇx jumps over
 4582        fox jumps over
 4583        tˇhe lazy dog"});
 4584}
 4585
 4586#[gpui::test]
 4587async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4588    init_test(cx, |_| {});
 4589
 4590    let mut cx = EditorTestContext::new(cx).await;
 4591    let language = Arc::new(Language::new(
 4592        LanguageConfig::default(),
 4593        Some(tree_sitter_rust::LANGUAGE.into()),
 4594    ));
 4595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4596
 4597    // Cut an indented block, without the leading whitespace.
 4598    cx.set_state(indoc! {"
 4599        const a: B = (
 4600            c(),
 4601            «d(
 4602                e,
 4603                f
 4604            )ˇ»
 4605        );
 4606    "});
 4607    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4608    cx.assert_editor_state(indoc! {"
 4609        const a: B = (
 4610            c(),
 4611            ˇ
 4612        );
 4613    "});
 4614
 4615    // Paste it at the same position.
 4616    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4617    cx.assert_editor_state(indoc! {"
 4618        const a: B = (
 4619            c(),
 4620            d(
 4621                e,
 4622                f
 4623 4624        );
 4625    "});
 4626
 4627    // Paste it at a line with a lower indent level.
 4628    cx.set_state(indoc! {"
 4629        ˇ
 4630        const a: B = (
 4631            c(),
 4632        );
 4633    "});
 4634    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4635    cx.assert_editor_state(indoc! {"
 4636        d(
 4637            e,
 4638            f
 4639 4640        const a: B = (
 4641            c(),
 4642        );
 4643    "});
 4644
 4645    // Cut an indented block, with the leading whitespace.
 4646    cx.set_state(indoc! {"
 4647        const a: B = (
 4648            c(),
 4649        «    d(
 4650                e,
 4651                f
 4652            )
 4653        ˇ»);
 4654    "});
 4655    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4656    cx.assert_editor_state(indoc! {"
 4657        const a: B = (
 4658            c(),
 4659        ˇ);
 4660    "});
 4661
 4662    // Paste it at the same position.
 4663    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        const a: B = (
 4666            c(),
 4667            d(
 4668                e,
 4669                f
 4670            )
 4671        ˇ);
 4672    "});
 4673
 4674    // Paste it at a line with a higher indent level.
 4675    cx.set_state(indoc! {"
 4676        const a: B = (
 4677            c(),
 4678            d(
 4679                e,
 4680 4681            )
 4682        );
 4683    "});
 4684    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4685    cx.assert_editor_state(indoc! {"
 4686        const a: B = (
 4687            c(),
 4688            d(
 4689                e,
 4690                f    d(
 4691                    e,
 4692                    f
 4693                )
 4694        ˇ
 4695            )
 4696        );
 4697    "});
 4698}
 4699
 4700#[gpui::test]
 4701fn test_select_all(cx: &mut TestAppContext) {
 4702    init_test(cx, |_| {});
 4703
 4704    let view = cx.add_window(|cx| {
 4705        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4706        build_editor(buffer, cx)
 4707    });
 4708    _ = view.update(cx, |view, cx| {
 4709        view.select_all(&SelectAll, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4713        );
 4714    });
 4715}
 4716
 4717#[gpui::test]
 4718fn test_select_line(cx: &mut TestAppContext) {
 4719    init_test(cx, |_| {});
 4720
 4721    let view = cx.add_window(|cx| {
 4722        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4723        build_editor(buffer, cx)
 4724    });
 4725    _ = view.update(cx, |view, cx| {
 4726        view.change_selections(None, cx, |s| {
 4727            s.select_display_ranges([
 4728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4730                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4731                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4732            ])
 4733        });
 4734        view.select_line(&SelectLine, cx);
 4735        assert_eq!(
 4736            view.selections.display_ranges(cx),
 4737            vec![
 4738                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4739                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4740            ]
 4741        );
 4742    });
 4743
 4744    _ = view.update(cx, |view, cx| {
 4745        view.select_line(&SelectLine, cx);
 4746        assert_eq!(
 4747            view.selections.display_ranges(cx),
 4748            vec![
 4749                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4750                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4751            ]
 4752        );
 4753    });
 4754
 4755    _ = view.update(cx, |view, cx| {
 4756        view.select_line(&SelectLine, cx);
 4757        assert_eq!(
 4758            view.selections.display_ranges(cx),
 4759            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4760        );
 4761    });
 4762}
 4763
 4764#[gpui::test]
 4765fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4766    init_test(cx, |_| {});
 4767
 4768    let view = cx.add_window(|cx| {
 4769        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4770        build_editor(buffer, cx)
 4771    });
 4772    _ = view.update(cx, |view, cx| {
 4773        view.fold_creases(
 4774            vec![
 4775                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4776                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4777                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4778            ],
 4779            true,
 4780            cx,
 4781        );
 4782        view.change_selections(None, cx, |s| {
 4783            s.select_display_ranges([
 4784                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4785                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4786                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4787                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4788            ])
 4789        });
 4790        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4791    });
 4792
 4793    _ = view.update(cx, |view, cx| {
 4794        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4795        assert_eq!(
 4796            view.display_text(cx),
 4797            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4798        );
 4799        assert_eq!(
 4800            view.selections.display_ranges(cx),
 4801            [
 4802                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4803                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4804                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4805                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4806            ]
 4807        );
 4808    });
 4809
 4810    _ = view.update(cx, |view, cx| {
 4811        view.change_selections(None, cx, |s| {
 4812            s.select_display_ranges([
 4813                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4814            ])
 4815        });
 4816        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4817        assert_eq!(
 4818            view.display_text(cx),
 4819            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4820        );
 4821        assert_eq!(
 4822            view.selections.display_ranges(cx),
 4823            [
 4824                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4825                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4826                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4827                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4828                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4829                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4830                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4831                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4832            ]
 4833        );
 4834    });
 4835}
 4836
 4837#[gpui::test]
 4838async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4839    init_test(cx, |_| {});
 4840
 4841    let mut cx = EditorTestContext::new(cx).await;
 4842
 4843    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4844    cx.set_state(indoc!(
 4845        r#"abc
 4846           defˇghi
 4847
 4848           jk
 4849           nlmo
 4850           "#
 4851    ));
 4852
 4853    cx.update_editor(|editor, cx| {
 4854        editor.add_selection_above(&Default::default(), cx);
 4855    });
 4856
 4857    cx.assert_editor_state(indoc!(
 4858        r#"abcˇ
 4859           defˇghi
 4860
 4861           jk
 4862           nlmo
 4863           "#
 4864    ));
 4865
 4866    cx.update_editor(|editor, cx| {
 4867        editor.add_selection_above(&Default::default(), cx);
 4868    });
 4869
 4870    cx.assert_editor_state(indoc!(
 4871        r#"abcˇ
 4872            defˇghi
 4873
 4874            jk
 4875            nlmo
 4876            "#
 4877    ));
 4878
 4879    cx.update_editor(|view, cx| {
 4880        view.add_selection_below(&Default::default(), cx);
 4881    });
 4882
 4883    cx.assert_editor_state(indoc!(
 4884        r#"abc
 4885           defˇghi
 4886
 4887           jk
 4888           nlmo
 4889           "#
 4890    ));
 4891
 4892    cx.update_editor(|view, cx| {
 4893        view.undo_selection(&Default::default(), cx);
 4894    });
 4895
 4896    cx.assert_editor_state(indoc!(
 4897        r#"abcˇ
 4898           defˇghi
 4899
 4900           jk
 4901           nlmo
 4902           "#
 4903    ));
 4904
 4905    cx.update_editor(|view, cx| {
 4906        view.redo_selection(&Default::default(), cx);
 4907    });
 4908
 4909    cx.assert_editor_state(indoc!(
 4910        r#"abc
 4911           defˇghi
 4912
 4913           jk
 4914           nlmo
 4915           "#
 4916    ));
 4917
 4918    cx.update_editor(|view, cx| {
 4919        view.add_selection_below(&Default::default(), cx);
 4920    });
 4921
 4922    cx.assert_editor_state(indoc!(
 4923        r#"abc
 4924           defˇghi
 4925
 4926           jk
 4927           nlmˇo
 4928           "#
 4929    ));
 4930
 4931    cx.update_editor(|view, cx| {
 4932        view.add_selection_below(&Default::default(), cx);
 4933    });
 4934
 4935    cx.assert_editor_state(indoc!(
 4936        r#"abc
 4937           defˇghi
 4938
 4939           jk
 4940           nlmˇo
 4941           "#
 4942    ));
 4943
 4944    // change selections
 4945    cx.set_state(indoc!(
 4946        r#"abc
 4947           def«ˇg»hi
 4948
 4949           jk
 4950           nlmo
 4951           "#
 4952    ));
 4953
 4954    cx.update_editor(|view, cx| {
 4955        view.add_selection_below(&Default::default(), cx);
 4956    });
 4957
 4958    cx.assert_editor_state(indoc!(
 4959        r#"abc
 4960           def«ˇg»hi
 4961
 4962           jk
 4963           nlm«ˇo»
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|view, cx| {
 4968        view.add_selection_below(&Default::default(), cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abc
 4973           def«ˇg»hi
 4974
 4975           jk
 4976           nlm«ˇo»
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|view, cx| {
 4981        view.add_selection_above(&Default::default(), cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abc
 4986           def«ˇg»hi
 4987
 4988           jk
 4989           nlmo
 4990           "#
 4991    ));
 4992
 4993    cx.update_editor(|view, cx| {
 4994        view.add_selection_above(&Default::default(), cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"abc
 4999           def«ˇg»hi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    // Change selections again
 5007    cx.set_state(indoc!(
 5008        r#"a«bc
 5009           defgˇ»hi
 5010
 5011           jk
 5012           nlmo
 5013           "#
 5014    ));
 5015
 5016    cx.update_editor(|view, cx| {
 5017        view.add_selection_below(&Default::default(), cx);
 5018    });
 5019
 5020    cx.assert_editor_state(indoc!(
 5021        r#"a«bcˇ»
 5022           d«efgˇ»hi
 5023
 5024           j«kˇ»
 5025           nlmo
 5026           "#
 5027    ));
 5028
 5029    cx.update_editor(|view, cx| {
 5030        view.add_selection_below(&Default::default(), cx);
 5031    });
 5032    cx.assert_editor_state(indoc!(
 5033        r#"a«bcˇ»
 5034           d«efgˇ»hi
 5035
 5036           j«kˇ»
 5037           n«lmoˇ»
 5038           "#
 5039    ));
 5040    cx.update_editor(|view, cx| {
 5041        view.add_selection_above(&Default::default(), cx);
 5042    });
 5043
 5044    cx.assert_editor_state(indoc!(
 5045        r#"a«bcˇ»
 5046           d«efgˇ»hi
 5047
 5048           j«kˇ»
 5049           nlmo
 5050           "#
 5051    ));
 5052
 5053    // Change selections again
 5054    cx.set_state(indoc!(
 5055        r#"abc
 5056           d«ˇefghi
 5057
 5058           jk
 5059           nlm»o
 5060           "#
 5061    ));
 5062
 5063    cx.update_editor(|view, cx| {
 5064        view.add_selection_above(&Default::default(), cx);
 5065    });
 5066
 5067    cx.assert_editor_state(indoc!(
 5068        r#"a«ˇbc»
 5069           d«ˇef»ghi
 5070
 5071           j«ˇk»
 5072           n«ˇlm»o
 5073           "#
 5074    ));
 5075
 5076    cx.update_editor(|view, cx| {
 5077        view.add_selection_below(&Default::default(), cx);
 5078    });
 5079
 5080    cx.assert_editor_state(indoc!(
 5081        r#"abc
 5082           d«ˇef»ghi
 5083
 5084           j«ˇk»
 5085           n«ˇlm»o
 5086           "#
 5087    ));
 5088}
 5089
 5090#[gpui::test]
 5091async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5092    init_test(cx, |_| {});
 5093
 5094    let mut cx = EditorTestContext::new(cx).await;
 5095    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5096
 5097    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5098        .unwrap();
 5099    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5104
 5105    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5106    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5107
 5108    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5109    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5110
 5111    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5112        .unwrap();
 5113    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5114
 5115    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5118}
 5119
 5120#[gpui::test]
 5121async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5122    init_test(cx, |_| {});
 5123
 5124    let mut cx = EditorTestContext::new(cx).await;
 5125    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5126
 5127    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5128        .unwrap();
 5129    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5130}
 5131
 5132#[gpui::test]
 5133async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5134    init_test(cx, |_| {});
 5135
 5136    let mut cx = EditorTestContext::new(cx).await;
 5137    cx.set_state(
 5138        r#"let foo = 2;
 5139lˇet foo = 2;
 5140let fooˇ = 2;
 5141let foo = 2;
 5142let foo = ˇ2;"#,
 5143    );
 5144
 5145    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5146        .unwrap();
 5147    cx.assert_editor_state(
 5148        r#"let foo = 2;
 5149«letˇ» foo = 2;
 5150let «fooˇ» = 2;
 5151let foo = 2;
 5152let foo = «2ˇ»;"#,
 5153    );
 5154
 5155    // noop for multiple selections with different contents
 5156    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5157        .unwrap();
 5158    cx.assert_editor_state(
 5159        r#"let foo = 2;
 5160«letˇ» foo = 2;
 5161let «fooˇ» = 2;
 5162let foo = 2;
 5163let foo = «2ˇ»;"#,
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let mut cx =
 5172        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5173
 5174    cx.assert_editor_state(indoc! {"
 5175        ˇbbb
 5176        ccc
 5177
 5178        bbb
 5179        ccc
 5180        "});
 5181    cx.dispatch_action(SelectPrevious::default());
 5182    cx.assert_editor_state(indoc! {"
 5183                «bbbˇ»
 5184                ccc
 5185
 5186                bbb
 5187                ccc
 5188                "});
 5189    cx.dispatch_action(SelectPrevious::default());
 5190    cx.assert_editor_state(indoc! {"
 5191                «bbbˇ»
 5192                ccc
 5193
 5194                «bbbˇ»
 5195                ccc
 5196                "});
 5197}
 5198
 5199#[gpui::test]
 5200async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5201    init_test(cx, |_| {});
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5205
 5206    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5207        .unwrap();
 5208    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5209
 5210    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5211        .unwrap();
 5212    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5213
 5214    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5215    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5216
 5217    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5218    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5219
 5220    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5221        .unwrap();
 5222    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5223
 5224    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5225        .unwrap();
 5226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5227
 5228    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5229        .unwrap();
 5230    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5231}
 5232
 5233#[gpui::test]
 5234async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5235    init_test(cx, |_| {});
 5236
 5237    let mut cx = EditorTestContext::new(cx).await;
 5238    cx.set_state(
 5239        r#"let foo = 2;
 5240lˇet foo = 2;
 5241let fooˇ = 2;
 5242let foo = 2;
 5243let foo = ˇ2;"#,
 5244    );
 5245
 5246    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5247        .unwrap();
 5248    cx.assert_editor_state(
 5249        r#"let foo = 2;
 5250«letˇ» foo = 2;
 5251let «fooˇ» = 2;
 5252let foo = 2;
 5253let foo = «2ˇ»;"#,
 5254    );
 5255
 5256    // noop for multiple selections with different contents
 5257    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5258        .unwrap();
 5259    cx.assert_editor_state(
 5260        r#"let foo = 2;
 5261«letˇ» foo = 2;
 5262let «fooˇ» = 2;
 5263let foo = 2;
 5264let foo = «2ˇ»;"#,
 5265    );
 5266}
 5267
 5268#[gpui::test]
 5269async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5270    init_test(cx, |_| {});
 5271
 5272    let mut cx = EditorTestContext::new(cx).await;
 5273    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5274
 5275    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5276        .unwrap();
 5277    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5278
 5279    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5280        .unwrap();
 5281    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5282
 5283    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5284    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5285
 5286    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5287    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5288
 5289    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5290        .unwrap();
 5291    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5292
 5293    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5294        .unwrap();
 5295    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5296}
 5297
 5298#[gpui::test]
 5299async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5300    init_test(cx, |_| {});
 5301
 5302    let language = Arc::new(Language::new(
 5303        LanguageConfig::default(),
 5304        Some(tree_sitter_rust::LANGUAGE.into()),
 5305    ));
 5306
 5307    let text = r#"
 5308        use mod1::mod2::{mod3, mod4};
 5309
 5310        fn fn_1(param1: bool, param2: &str) {
 5311            let var1 = "text";
 5312        }
 5313    "#
 5314    .unindent();
 5315
 5316    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5317    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5318    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5319
 5320    editor
 5321        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5322        .await;
 5323
 5324    editor.update(cx, |view, cx| {
 5325        view.change_selections(None, cx, |s| {
 5326            s.select_display_ranges([
 5327                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5328                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5329                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5330            ]);
 5331        });
 5332        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5333    });
 5334    editor.update(cx, |editor, cx| {
 5335        assert_text_with_selections(
 5336            editor,
 5337            indoc! {r#"
 5338                use mod1::mod2::{mod3, «mod4ˇ»};
 5339
 5340                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5341                    let var1 = "«textˇ»";
 5342                }
 5343            "#},
 5344            cx,
 5345        );
 5346    });
 5347
 5348    editor.update(cx, |view, cx| {
 5349        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5350    });
 5351    editor.update(cx, |editor, cx| {
 5352        assert_text_with_selections(
 5353            editor,
 5354            indoc! {r#"
 5355                use mod1::mod2::«{mod3, mod4}ˇ»;
 5356
 5357                «ˇfn fn_1(param1: bool, param2: &str) {
 5358                    let var1 = "text";
 5359 5360            "#},
 5361            cx,
 5362        );
 5363    });
 5364
 5365    editor.update(cx, |view, cx| {
 5366        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5367    });
 5368    assert_eq!(
 5369        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5370        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5371    );
 5372
 5373    // Trying to expand the selected syntax node one more time has no effect.
 5374    editor.update(cx, |view, cx| {
 5375        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5376    });
 5377    assert_eq!(
 5378        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5379        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5380    );
 5381
 5382    editor.update(cx, |view, cx| {
 5383        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5384    });
 5385    editor.update(cx, |editor, cx| {
 5386        assert_text_with_selections(
 5387            editor,
 5388            indoc! {r#"
 5389                use mod1::mod2::«{mod3, mod4}ˇ»;
 5390
 5391                «ˇfn fn_1(param1: bool, param2: &str) {
 5392                    let var1 = "text";
 5393 5394            "#},
 5395            cx,
 5396        );
 5397    });
 5398
 5399    editor.update(cx, |view, cx| {
 5400        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5401    });
 5402    editor.update(cx, |editor, cx| {
 5403        assert_text_with_selections(
 5404            editor,
 5405            indoc! {r#"
 5406                use mod1::mod2::{mod3, «mod4ˇ»};
 5407
 5408                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5409                    let var1 = "«textˇ»";
 5410                }
 5411            "#},
 5412            cx,
 5413        );
 5414    });
 5415
 5416    editor.update(cx, |view, cx| {
 5417        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5418    });
 5419    editor.update(cx, |editor, cx| {
 5420        assert_text_with_selections(
 5421            editor,
 5422            indoc! {r#"
 5423                use mod1::mod2::{mod3, mo«ˇ»d4};
 5424
 5425                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5426                    let var1 = "te«ˇ»xt";
 5427                }
 5428            "#},
 5429            cx,
 5430        );
 5431    });
 5432
 5433    // Trying to shrink the selected syntax node one more time has no effect.
 5434    editor.update(cx, |view, cx| {
 5435        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5436    });
 5437    editor.update(cx, |editor, cx| {
 5438        assert_text_with_selections(
 5439            editor,
 5440            indoc! {r#"
 5441                use mod1::mod2::{mod3, mo«ˇ»d4};
 5442
 5443                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5444                    let var1 = "te«ˇ»xt";
 5445                }
 5446            "#},
 5447            cx,
 5448        );
 5449    });
 5450
 5451    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5452    // a fold.
 5453    editor.update(cx, |view, cx| {
 5454        view.fold_creases(
 5455            vec![
 5456                Crease::simple(
 5457                    Point::new(0, 21)..Point::new(0, 24),
 5458                    FoldPlaceholder::test(),
 5459                ),
 5460                Crease::simple(
 5461                    Point::new(3, 20)..Point::new(3, 22),
 5462                    FoldPlaceholder::test(),
 5463                ),
 5464            ],
 5465            true,
 5466            cx,
 5467        );
 5468        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5469    });
 5470    editor.update(cx, |editor, cx| {
 5471        assert_text_with_selections(
 5472            editor,
 5473            indoc! {r#"
 5474                use mod1::mod2::«{mod3, mod4}ˇ»;
 5475
 5476                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5477                    «let var1 = "text";ˇ»
 5478                }
 5479            "#},
 5480            cx,
 5481        );
 5482    });
 5483}
 5484
 5485#[gpui::test]
 5486async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5487    init_test(cx, |_| {});
 5488
 5489    let language = Arc::new(
 5490        Language::new(
 5491            LanguageConfig {
 5492                brackets: BracketPairConfig {
 5493                    pairs: vec![
 5494                        BracketPair {
 5495                            start: "{".to_string(),
 5496                            end: "}".to_string(),
 5497                            close: false,
 5498                            surround: false,
 5499                            newline: true,
 5500                        },
 5501                        BracketPair {
 5502                            start: "(".to_string(),
 5503                            end: ")".to_string(),
 5504                            close: false,
 5505                            surround: false,
 5506                            newline: true,
 5507                        },
 5508                    ],
 5509                    ..Default::default()
 5510                },
 5511                ..Default::default()
 5512            },
 5513            Some(tree_sitter_rust::LANGUAGE.into()),
 5514        )
 5515        .with_indents_query(
 5516            r#"
 5517                (_ "(" ")" @end) @indent
 5518                (_ "{" "}" @end) @indent
 5519            "#,
 5520        )
 5521        .unwrap(),
 5522    );
 5523
 5524    let text = "fn a() {}";
 5525
 5526    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5527    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5528    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5529    editor
 5530        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5531        .await;
 5532
 5533    editor.update(cx, |editor, cx| {
 5534        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5535        editor.newline(&Newline, cx);
 5536        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5537        assert_eq!(
 5538            editor.selections.ranges(cx),
 5539            &[
 5540                Point::new(1, 4)..Point::new(1, 4),
 5541                Point::new(3, 4)..Point::new(3, 4),
 5542                Point::new(5, 0)..Point::new(5, 0)
 5543            ]
 5544        );
 5545    });
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    {
 5553        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5554        cx.set_state(indoc! {"
 5555            impl A {
 5556
 5557                fn b() {}
 5558
 5559            «fn c() {
 5560
 5561            }ˇ»
 5562            }
 5563        "});
 5564
 5565        cx.update_editor(|editor, cx| {
 5566            editor.autoindent(&Default::default(), cx);
 5567        });
 5568
 5569        cx.assert_editor_state(indoc! {"
 5570            impl A {
 5571
 5572                fn b() {}
 5573
 5574                «fn c() {
 5575
 5576                }ˇ»
 5577            }
 5578        "});
 5579    }
 5580
 5581    {
 5582        let mut cx = EditorTestContext::new_multibuffer(
 5583            cx,
 5584            [indoc! { "
 5585                impl A {
 5586                «
 5587                // a
 5588                fn b(){}
 5589                »
 5590                «
 5591                    }
 5592                    fn c(){}
 5593                »
 5594            "}],
 5595        );
 5596
 5597        let buffer = cx.update_editor(|editor, cx| {
 5598            let buffer = editor.buffer().update(cx, |buffer, _| {
 5599                buffer.all_buffers().iter().next().unwrap().clone()
 5600            });
 5601            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5602            buffer
 5603        });
 5604
 5605        cx.run_until_parked();
 5606        cx.update_editor(|editor, cx| {
 5607            editor.select_all(&Default::default(), cx);
 5608            editor.autoindent(&Default::default(), cx)
 5609        });
 5610        cx.run_until_parked();
 5611
 5612        cx.update(|cx| {
 5613            pretty_assertions::assert_eq!(
 5614                buffer.read(cx).text(),
 5615                indoc! { "
 5616                    impl A {
 5617
 5618                        // a
 5619                        fn b(){}
 5620
 5621
 5622                    }
 5623                    fn c(){}
 5624
 5625                " }
 5626            )
 5627        });
 5628    }
 5629}
 5630
 5631#[gpui::test]
 5632async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5633    init_test(cx, |_| {});
 5634
 5635    let mut cx = EditorTestContext::new(cx).await;
 5636
 5637    let language = Arc::new(Language::new(
 5638        LanguageConfig {
 5639            brackets: BracketPairConfig {
 5640                pairs: vec![
 5641                    BracketPair {
 5642                        start: "{".to_string(),
 5643                        end: "}".to_string(),
 5644                        close: true,
 5645                        surround: true,
 5646                        newline: true,
 5647                    },
 5648                    BracketPair {
 5649                        start: "(".to_string(),
 5650                        end: ")".to_string(),
 5651                        close: true,
 5652                        surround: true,
 5653                        newline: true,
 5654                    },
 5655                    BracketPair {
 5656                        start: "/*".to_string(),
 5657                        end: " */".to_string(),
 5658                        close: true,
 5659                        surround: true,
 5660                        newline: true,
 5661                    },
 5662                    BracketPair {
 5663                        start: "[".to_string(),
 5664                        end: "]".to_string(),
 5665                        close: false,
 5666                        surround: false,
 5667                        newline: true,
 5668                    },
 5669                    BracketPair {
 5670                        start: "\"".to_string(),
 5671                        end: "\"".to_string(),
 5672                        close: true,
 5673                        surround: true,
 5674                        newline: false,
 5675                    },
 5676                    BracketPair {
 5677                        start: "<".to_string(),
 5678                        end: ">".to_string(),
 5679                        close: false,
 5680                        surround: true,
 5681                        newline: true,
 5682                    },
 5683                ],
 5684                ..Default::default()
 5685            },
 5686            autoclose_before: "})]".to_string(),
 5687            ..Default::default()
 5688        },
 5689        Some(tree_sitter_rust::LANGUAGE.into()),
 5690    ));
 5691
 5692    cx.language_registry().add(language.clone());
 5693    cx.update_buffer(|buffer, cx| {
 5694        buffer.set_language(Some(language), cx);
 5695    });
 5696
 5697    cx.set_state(
 5698        &r#"
 5699            🏀ˇ
 5700            εˇ
 5701            ❤️ˇ
 5702        "#
 5703        .unindent(),
 5704    );
 5705
 5706    // autoclose multiple nested brackets at multiple cursors
 5707    cx.update_editor(|view, cx| {
 5708        view.handle_input("{", cx);
 5709        view.handle_input("{", cx);
 5710        view.handle_input("{", cx);
 5711    });
 5712    cx.assert_editor_state(
 5713        &"
 5714            🏀{{{ˇ}}}
 5715            ε{{{ˇ}}}
 5716            ❤️{{{ˇ}}}
 5717        "
 5718        .unindent(),
 5719    );
 5720
 5721    // insert a different closing bracket
 5722    cx.update_editor(|view, cx| {
 5723        view.handle_input(")", cx);
 5724    });
 5725    cx.assert_editor_state(
 5726        &"
 5727            🏀{{{)ˇ}}}
 5728            ε{{{)ˇ}}}
 5729            ❤️{{{)ˇ}}}
 5730        "
 5731        .unindent(),
 5732    );
 5733
 5734    // skip over the auto-closed brackets when typing a closing bracket
 5735    cx.update_editor(|view, cx| {
 5736        view.move_right(&MoveRight, cx);
 5737        view.handle_input("}", cx);
 5738        view.handle_input("}", cx);
 5739        view.handle_input("}", cx);
 5740    });
 5741    cx.assert_editor_state(
 5742        &"
 5743            🏀{{{)}}}}ˇ
 5744            ε{{{)}}}}ˇ
 5745            ❤️{{{)}}}}ˇ
 5746        "
 5747        .unindent(),
 5748    );
 5749
 5750    // autoclose multi-character pairs
 5751    cx.set_state(
 5752        &"
 5753            ˇ
 5754            ˇ
 5755        "
 5756        .unindent(),
 5757    );
 5758    cx.update_editor(|view, cx| {
 5759        view.handle_input("/", cx);
 5760        view.handle_input("*", cx);
 5761    });
 5762    cx.assert_editor_state(
 5763        &"
 5764            /*ˇ */
 5765            /*ˇ */
 5766        "
 5767        .unindent(),
 5768    );
 5769
 5770    // one cursor autocloses a multi-character pair, one cursor
 5771    // does not autoclose.
 5772    cx.set_state(
 5773        &"
 5774 5775            ˇ
 5776        "
 5777        .unindent(),
 5778    );
 5779    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5780    cx.assert_editor_state(
 5781        &"
 5782            /*ˇ */
 5783 5784        "
 5785        .unindent(),
 5786    );
 5787
 5788    // Don't autoclose if the next character isn't whitespace and isn't
 5789    // listed in the language's "autoclose_before" section.
 5790    cx.set_state("ˇa b");
 5791    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5792    cx.assert_editor_state("{ˇa b");
 5793
 5794    // Don't autoclose if `close` is false for the bracket pair
 5795    cx.set_state("ˇ");
 5796    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5797    cx.assert_editor_state("");
 5798
 5799    // Surround with brackets if text is selected
 5800    cx.set_state("«aˇ» b");
 5801    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5802    cx.assert_editor_state("{«aˇ»} b");
 5803
 5804    // Autclose pair where the start and end characters are the same
 5805    cx.set_state("");
 5806    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5807    cx.assert_editor_state("a\"ˇ\"");
 5808    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5809    cx.assert_editor_state("a\"\"ˇ");
 5810
 5811    // Don't autoclose pair if autoclose is disabled
 5812    cx.set_state("ˇ");
 5813    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5814    cx.assert_editor_state("");
 5815
 5816    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5817    cx.set_state("«aˇ» b");
 5818    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5819    cx.assert_editor_state("<«aˇ»> b");
 5820}
 5821
 5822#[gpui::test]
 5823async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5824    init_test(cx, |settings| {
 5825        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5826    });
 5827
 5828    let mut cx = EditorTestContext::new(cx).await;
 5829
 5830    let language = Arc::new(Language::new(
 5831        LanguageConfig {
 5832            brackets: BracketPairConfig {
 5833                pairs: vec![
 5834                    BracketPair {
 5835                        start: "{".to_string(),
 5836                        end: "}".to_string(),
 5837                        close: true,
 5838                        surround: true,
 5839                        newline: true,
 5840                    },
 5841                    BracketPair {
 5842                        start: "(".to_string(),
 5843                        end: ")".to_string(),
 5844                        close: true,
 5845                        surround: true,
 5846                        newline: true,
 5847                    },
 5848                    BracketPair {
 5849                        start: "[".to_string(),
 5850                        end: "]".to_string(),
 5851                        close: false,
 5852                        surround: false,
 5853                        newline: true,
 5854                    },
 5855                ],
 5856                ..Default::default()
 5857            },
 5858            autoclose_before: "})]".to_string(),
 5859            ..Default::default()
 5860        },
 5861        Some(tree_sitter_rust::LANGUAGE.into()),
 5862    ));
 5863
 5864    cx.language_registry().add(language.clone());
 5865    cx.update_buffer(|buffer, cx| {
 5866        buffer.set_language(Some(language), cx);
 5867    });
 5868
 5869    cx.set_state(
 5870        &"
 5871            ˇ
 5872            ˇ
 5873            ˇ
 5874        "
 5875        .unindent(),
 5876    );
 5877
 5878    // ensure only matching closing brackets are skipped over
 5879    cx.update_editor(|view, cx| {
 5880        view.handle_input("}", cx);
 5881        view.move_left(&MoveLeft, cx);
 5882        view.handle_input(")", cx);
 5883        view.move_left(&MoveLeft, cx);
 5884    });
 5885    cx.assert_editor_state(
 5886        &"
 5887            ˇ)}
 5888            ˇ)}
 5889            ˇ)}
 5890        "
 5891        .unindent(),
 5892    );
 5893
 5894    // skip-over closing brackets at multiple cursors
 5895    cx.update_editor(|view, cx| {
 5896        view.handle_input(")", cx);
 5897        view.handle_input("}", cx);
 5898    });
 5899    cx.assert_editor_state(
 5900        &"
 5901            )}ˇ
 5902            )}ˇ
 5903            )}ˇ
 5904        "
 5905        .unindent(),
 5906    );
 5907
 5908    // ignore non-close brackets
 5909    cx.update_editor(|view, cx| {
 5910        view.handle_input("]", cx);
 5911        view.move_left(&MoveLeft, cx);
 5912        view.handle_input("]", cx);
 5913    });
 5914    cx.assert_editor_state(
 5915        &"
 5916            )}]ˇ]
 5917            )}]ˇ]
 5918            )}]ˇ]
 5919        "
 5920        .unindent(),
 5921    );
 5922}
 5923
 5924#[gpui::test]
 5925async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5926    init_test(cx, |_| {});
 5927
 5928    let mut cx = EditorTestContext::new(cx).await;
 5929
 5930    let html_language = Arc::new(
 5931        Language::new(
 5932            LanguageConfig {
 5933                name: "HTML".into(),
 5934                brackets: BracketPairConfig {
 5935                    pairs: vec![
 5936                        BracketPair {
 5937                            start: "<".into(),
 5938                            end: ">".into(),
 5939                            close: true,
 5940                            ..Default::default()
 5941                        },
 5942                        BracketPair {
 5943                            start: "{".into(),
 5944                            end: "}".into(),
 5945                            close: true,
 5946                            ..Default::default()
 5947                        },
 5948                        BracketPair {
 5949                            start: "(".into(),
 5950                            end: ")".into(),
 5951                            close: true,
 5952                            ..Default::default()
 5953                        },
 5954                    ],
 5955                    ..Default::default()
 5956                },
 5957                autoclose_before: "})]>".into(),
 5958                ..Default::default()
 5959            },
 5960            Some(tree_sitter_html::language()),
 5961        )
 5962        .with_injection_query(
 5963            r#"
 5964            (script_element
 5965                (raw_text) @content
 5966                (#set! "language" "javascript"))
 5967            "#,
 5968        )
 5969        .unwrap(),
 5970    );
 5971
 5972    let javascript_language = Arc::new(Language::new(
 5973        LanguageConfig {
 5974            name: "JavaScript".into(),
 5975            brackets: BracketPairConfig {
 5976                pairs: vec![
 5977                    BracketPair {
 5978                        start: "/*".into(),
 5979                        end: " */".into(),
 5980                        close: true,
 5981                        ..Default::default()
 5982                    },
 5983                    BracketPair {
 5984                        start: "{".into(),
 5985                        end: "}".into(),
 5986                        close: true,
 5987                        ..Default::default()
 5988                    },
 5989                    BracketPair {
 5990                        start: "(".into(),
 5991                        end: ")".into(),
 5992                        close: true,
 5993                        ..Default::default()
 5994                    },
 5995                ],
 5996                ..Default::default()
 5997            },
 5998            autoclose_before: "})]>".into(),
 5999            ..Default::default()
 6000        },
 6001        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6002    ));
 6003
 6004    cx.language_registry().add(html_language.clone());
 6005    cx.language_registry().add(javascript_language.clone());
 6006
 6007    cx.update_buffer(|buffer, cx| {
 6008        buffer.set_language(Some(html_language), cx);
 6009    });
 6010
 6011    cx.set_state(
 6012        &r#"
 6013            <body>ˇ
 6014                <script>
 6015                    var x = 1;ˇ
 6016                </script>
 6017            </body>ˇ
 6018        "#
 6019        .unindent(),
 6020    );
 6021
 6022    // Precondition: different languages are active at different locations.
 6023    cx.update_editor(|editor, cx| {
 6024        let snapshot = editor.snapshot(cx);
 6025        let cursors = editor.selections.ranges::<usize>(cx);
 6026        let languages = cursors
 6027            .iter()
 6028            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6029            .collect::<Vec<_>>();
 6030        assert_eq!(
 6031            languages,
 6032            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6033        );
 6034    });
 6035
 6036    // Angle brackets autoclose in HTML, but not JavaScript.
 6037    cx.update_editor(|editor, cx| {
 6038        editor.handle_input("<", cx);
 6039        editor.handle_input("a", cx);
 6040    });
 6041    cx.assert_editor_state(
 6042        &r#"
 6043            <body><aˇ>
 6044                <script>
 6045                    var x = 1;<aˇ
 6046                </script>
 6047            </body><aˇ>
 6048        "#
 6049        .unindent(),
 6050    );
 6051
 6052    // Curly braces and parens autoclose in both HTML and JavaScript.
 6053    cx.update_editor(|editor, cx| {
 6054        editor.handle_input(" b=", cx);
 6055        editor.handle_input("{", cx);
 6056        editor.handle_input("c", cx);
 6057        editor.handle_input("(", cx);
 6058    });
 6059    cx.assert_editor_state(
 6060        &r#"
 6061            <body><a b={c(ˇ)}>
 6062                <script>
 6063                    var x = 1;<a b={c(ˇ)}
 6064                </script>
 6065            </body><a b={c(ˇ)}>
 6066        "#
 6067        .unindent(),
 6068    );
 6069
 6070    // Brackets that were already autoclosed are skipped.
 6071    cx.update_editor(|editor, cx| {
 6072        editor.handle_input(")", cx);
 6073        editor.handle_input("d", cx);
 6074        editor.handle_input("}", cx);
 6075    });
 6076    cx.assert_editor_state(
 6077        &r#"
 6078            <body><a b={c()d}ˇ>
 6079                <script>
 6080                    var x = 1;<a b={c()d}ˇ
 6081                </script>
 6082            </body><a b={c()d}ˇ>
 6083        "#
 6084        .unindent(),
 6085    );
 6086    cx.update_editor(|editor, cx| {
 6087        editor.handle_input(">", cx);
 6088    });
 6089    cx.assert_editor_state(
 6090        &r#"
 6091            <body><a b={c()d}>ˇ
 6092                <script>
 6093                    var x = 1;<a b={c()d}>ˇ
 6094                </script>
 6095            </body><a b={c()d}>ˇ
 6096        "#
 6097        .unindent(),
 6098    );
 6099
 6100    // Reset
 6101    cx.set_state(
 6102        &r#"
 6103            <body>ˇ
 6104                <script>
 6105                    var x = 1;ˇ
 6106                </script>
 6107            </body>ˇ
 6108        "#
 6109        .unindent(),
 6110    );
 6111
 6112    cx.update_editor(|editor, cx| {
 6113        editor.handle_input("<", cx);
 6114    });
 6115    cx.assert_editor_state(
 6116        &r#"
 6117            <body><ˇ>
 6118                <script>
 6119                    var x = 1;<ˇ
 6120                </script>
 6121            </body><ˇ>
 6122        "#
 6123        .unindent(),
 6124    );
 6125
 6126    // When backspacing, the closing angle brackets are removed.
 6127    cx.update_editor(|editor, cx| {
 6128        editor.backspace(&Backspace, cx);
 6129    });
 6130    cx.assert_editor_state(
 6131        &r#"
 6132            <body>ˇ
 6133                <script>
 6134                    var x = 1;ˇ
 6135                </script>
 6136            </body>ˇ
 6137        "#
 6138        .unindent(),
 6139    );
 6140
 6141    // Block comments autoclose in JavaScript, but not HTML.
 6142    cx.update_editor(|editor, cx| {
 6143        editor.handle_input("/", cx);
 6144        editor.handle_input("*", cx);
 6145    });
 6146    cx.assert_editor_state(
 6147        &r#"
 6148            <body>/*ˇ
 6149                <script>
 6150                    var x = 1;/*ˇ */
 6151                </script>
 6152            </body>/*ˇ
 6153        "#
 6154        .unindent(),
 6155    );
 6156}
 6157
 6158#[gpui::test]
 6159async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6160    init_test(cx, |_| {});
 6161
 6162    let mut cx = EditorTestContext::new(cx).await;
 6163
 6164    let rust_language = Arc::new(
 6165        Language::new(
 6166            LanguageConfig {
 6167                name: "Rust".into(),
 6168                brackets: serde_json::from_value(json!([
 6169                    { "start": "{", "end": "}", "close": true, "newline": true },
 6170                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6171                ]))
 6172                .unwrap(),
 6173                autoclose_before: "})]>".into(),
 6174                ..Default::default()
 6175            },
 6176            Some(tree_sitter_rust::LANGUAGE.into()),
 6177        )
 6178        .with_override_query("(string_literal) @string")
 6179        .unwrap(),
 6180    );
 6181
 6182    cx.language_registry().add(rust_language.clone());
 6183    cx.update_buffer(|buffer, cx| {
 6184        buffer.set_language(Some(rust_language), cx);
 6185    });
 6186
 6187    cx.set_state(
 6188        &r#"
 6189            let x = ˇ
 6190        "#
 6191        .unindent(),
 6192    );
 6193
 6194    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6195    cx.update_editor(|editor, cx| {
 6196        editor.handle_input("\"", cx);
 6197    });
 6198    cx.assert_editor_state(
 6199        &r#"
 6200            let x = "ˇ"
 6201        "#
 6202        .unindent(),
 6203    );
 6204
 6205    // Inserting another quotation mark. The cursor moves across the existing
 6206    // automatically-inserted quotation mark.
 6207    cx.update_editor(|editor, cx| {
 6208        editor.handle_input("\"", cx);
 6209    });
 6210    cx.assert_editor_state(
 6211        &r#"
 6212            let x = ""ˇ
 6213        "#
 6214        .unindent(),
 6215    );
 6216
 6217    // Reset
 6218    cx.set_state(
 6219        &r#"
 6220            let x = ˇ
 6221        "#
 6222        .unindent(),
 6223    );
 6224
 6225    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6226    cx.update_editor(|editor, cx| {
 6227        editor.handle_input("\"", cx);
 6228        editor.handle_input(" ", cx);
 6229        editor.move_left(&Default::default(), cx);
 6230        editor.handle_input("\\", cx);
 6231        editor.handle_input("\"", cx);
 6232    });
 6233    cx.assert_editor_state(
 6234        &r#"
 6235            let x = "\"ˇ "
 6236        "#
 6237        .unindent(),
 6238    );
 6239
 6240    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6241    // mark. Nothing is inserted.
 6242    cx.update_editor(|editor, cx| {
 6243        editor.move_right(&Default::default(), cx);
 6244        editor.handle_input("\"", cx);
 6245    });
 6246    cx.assert_editor_state(
 6247        &r#"
 6248            let x = "\" "ˇ
 6249        "#
 6250        .unindent(),
 6251    );
 6252}
 6253
 6254#[gpui::test]
 6255async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6256    init_test(cx, |_| {});
 6257
 6258    let language = Arc::new(Language::new(
 6259        LanguageConfig {
 6260            brackets: BracketPairConfig {
 6261                pairs: vec![
 6262                    BracketPair {
 6263                        start: "{".to_string(),
 6264                        end: "}".to_string(),
 6265                        close: true,
 6266                        surround: true,
 6267                        newline: true,
 6268                    },
 6269                    BracketPair {
 6270                        start: "/* ".to_string(),
 6271                        end: "*/".to_string(),
 6272                        close: true,
 6273                        surround: true,
 6274                        ..Default::default()
 6275                    },
 6276                ],
 6277                ..Default::default()
 6278            },
 6279            ..Default::default()
 6280        },
 6281        Some(tree_sitter_rust::LANGUAGE.into()),
 6282    ));
 6283
 6284    let text = r#"
 6285        a
 6286        b
 6287        c
 6288    "#
 6289    .unindent();
 6290
 6291    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6293    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6294    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6295        .await;
 6296
 6297    view.update(cx, |view, cx| {
 6298        view.change_selections(None, cx, |s| {
 6299            s.select_display_ranges([
 6300                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6301                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6302                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6303            ])
 6304        });
 6305
 6306        view.handle_input("{", cx);
 6307        view.handle_input("{", cx);
 6308        view.handle_input("{", cx);
 6309        assert_eq!(
 6310            view.text(cx),
 6311            "
 6312                {{{a}}}
 6313                {{{b}}}
 6314                {{{c}}}
 6315            "
 6316            .unindent()
 6317        );
 6318        assert_eq!(
 6319            view.selections.display_ranges(cx),
 6320            [
 6321                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6322                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6323                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6324            ]
 6325        );
 6326
 6327        view.undo(&Undo, cx);
 6328        view.undo(&Undo, cx);
 6329        view.undo(&Undo, cx);
 6330        assert_eq!(
 6331            view.text(cx),
 6332            "
 6333                a
 6334                b
 6335                c
 6336            "
 6337            .unindent()
 6338        );
 6339        assert_eq!(
 6340            view.selections.display_ranges(cx),
 6341            [
 6342                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6343                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6345            ]
 6346        );
 6347
 6348        // Ensure inserting the first character of a multi-byte bracket pair
 6349        // doesn't surround the selections with the bracket.
 6350        view.handle_input("/", cx);
 6351        assert_eq!(
 6352            view.text(cx),
 6353            "
 6354                /
 6355                /
 6356                /
 6357            "
 6358            .unindent()
 6359        );
 6360        assert_eq!(
 6361            view.selections.display_ranges(cx),
 6362            [
 6363                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6364                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6365                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6366            ]
 6367        );
 6368
 6369        view.undo(&Undo, cx);
 6370        assert_eq!(
 6371            view.text(cx),
 6372            "
 6373                a
 6374                b
 6375                c
 6376            "
 6377            .unindent()
 6378        );
 6379        assert_eq!(
 6380            view.selections.display_ranges(cx),
 6381            [
 6382                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6383                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6384                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6385            ]
 6386        );
 6387
 6388        // Ensure inserting the last character of a multi-byte bracket pair
 6389        // doesn't surround the selections with the bracket.
 6390        view.handle_input("*", cx);
 6391        assert_eq!(
 6392            view.text(cx),
 6393            "
 6394                *
 6395                *
 6396                *
 6397            "
 6398            .unindent()
 6399        );
 6400        assert_eq!(
 6401            view.selections.display_ranges(cx),
 6402            [
 6403                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6404                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6405                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6406            ]
 6407        );
 6408    });
 6409}
 6410
 6411#[gpui::test]
 6412async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6413    init_test(cx, |_| {});
 6414
 6415    let language = Arc::new(Language::new(
 6416        LanguageConfig {
 6417            brackets: BracketPairConfig {
 6418                pairs: vec![BracketPair {
 6419                    start: "{".to_string(),
 6420                    end: "}".to_string(),
 6421                    close: true,
 6422                    surround: true,
 6423                    newline: true,
 6424                }],
 6425                ..Default::default()
 6426            },
 6427            autoclose_before: "}".to_string(),
 6428            ..Default::default()
 6429        },
 6430        Some(tree_sitter_rust::LANGUAGE.into()),
 6431    ));
 6432
 6433    let text = r#"
 6434        a
 6435        b
 6436        c
 6437    "#
 6438    .unindent();
 6439
 6440    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6441    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6442    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6443    editor
 6444        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6445        .await;
 6446
 6447    editor.update(cx, |editor, cx| {
 6448        editor.change_selections(None, cx, |s| {
 6449            s.select_ranges([
 6450                Point::new(0, 1)..Point::new(0, 1),
 6451                Point::new(1, 1)..Point::new(1, 1),
 6452                Point::new(2, 1)..Point::new(2, 1),
 6453            ])
 6454        });
 6455
 6456        editor.handle_input("{", cx);
 6457        editor.handle_input("{", cx);
 6458        editor.handle_input("_", cx);
 6459        assert_eq!(
 6460            editor.text(cx),
 6461            "
 6462                a{{_}}
 6463                b{{_}}
 6464                c{{_}}
 6465            "
 6466            .unindent()
 6467        );
 6468        assert_eq!(
 6469            editor.selections.ranges::<Point>(cx),
 6470            [
 6471                Point::new(0, 4)..Point::new(0, 4),
 6472                Point::new(1, 4)..Point::new(1, 4),
 6473                Point::new(2, 4)..Point::new(2, 4)
 6474            ]
 6475        );
 6476
 6477        editor.backspace(&Default::default(), cx);
 6478        editor.backspace(&Default::default(), cx);
 6479        assert_eq!(
 6480            editor.text(cx),
 6481            "
 6482                a{}
 6483                b{}
 6484                c{}
 6485            "
 6486            .unindent()
 6487        );
 6488        assert_eq!(
 6489            editor.selections.ranges::<Point>(cx),
 6490            [
 6491                Point::new(0, 2)..Point::new(0, 2),
 6492                Point::new(1, 2)..Point::new(1, 2),
 6493                Point::new(2, 2)..Point::new(2, 2)
 6494            ]
 6495        );
 6496
 6497        editor.delete_to_previous_word_start(&Default::default(), cx);
 6498        assert_eq!(
 6499            editor.text(cx),
 6500            "
 6501                a
 6502                b
 6503                c
 6504            "
 6505            .unindent()
 6506        );
 6507        assert_eq!(
 6508            editor.selections.ranges::<Point>(cx),
 6509            [
 6510                Point::new(0, 1)..Point::new(0, 1),
 6511                Point::new(1, 1)..Point::new(1, 1),
 6512                Point::new(2, 1)..Point::new(2, 1)
 6513            ]
 6514        );
 6515    });
 6516}
 6517
 6518#[gpui::test]
 6519async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6520    init_test(cx, |settings| {
 6521        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6522    });
 6523
 6524    let mut cx = EditorTestContext::new(cx).await;
 6525
 6526    let language = Arc::new(Language::new(
 6527        LanguageConfig {
 6528            brackets: BracketPairConfig {
 6529                pairs: vec![
 6530                    BracketPair {
 6531                        start: "{".to_string(),
 6532                        end: "}".to_string(),
 6533                        close: true,
 6534                        surround: true,
 6535                        newline: true,
 6536                    },
 6537                    BracketPair {
 6538                        start: "(".to_string(),
 6539                        end: ")".to_string(),
 6540                        close: true,
 6541                        surround: true,
 6542                        newline: true,
 6543                    },
 6544                    BracketPair {
 6545                        start: "[".to_string(),
 6546                        end: "]".to_string(),
 6547                        close: false,
 6548                        surround: true,
 6549                        newline: true,
 6550                    },
 6551                ],
 6552                ..Default::default()
 6553            },
 6554            autoclose_before: "})]".to_string(),
 6555            ..Default::default()
 6556        },
 6557        Some(tree_sitter_rust::LANGUAGE.into()),
 6558    ));
 6559
 6560    cx.language_registry().add(language.clone());
 6561    cx.update_buffer(|buffer, cx| {
 6562        buffer.set_language(Some(language), cx);
 6563    });
 6564
 6565    cx.set_state(
 6566        &"
 6567            {(ˇ)}
 6568            [[ˇ]]
 6569            {(ˇ)}
 6570        "
 6571        .unindent(),
 6572    );
 6573
 6574    cx.update_editor(|view, cx| {
 6575        view.backspace(&Default::default(), cx);
 6576        view.backspace(&Default::default(), cx);
 6577    });
 6578
 6579    cx.assert_editor_state(
 6580        &"
 6581            ˇ
 6582            ˇ]]
 6583            ˇ
 6584        "
 6585        .unindent(),
 6586    );
 6587
 6588    cx.update_editor(|view, cx| {
 6589        view.handle_input("{", cx);
 6590        view.handle_input("{", cx);
 6591        view.move_right(&MoveRight, cx);
 6592        view.move_right(&MoveRight, cx);
 6593        view.move_left(&MoveLeft, cx);
 6594        view.move_left(&MoveLeft, cx);
 6595        view.backspace(&Default::default(), cx);
 6596    });
 6597
 6598    cx.assert_editor_state(
 6599        &"
 6600            {ˇ}
 6601            {ˇ}]]
 6602            {ˇ}
 6603        "
 6604        .unindent(),
 6605    );
 6606
 6607    cx.update_editor(|view, cx| {
 6608        view.backspace(&Default::default(), cx);
 6609    });
 6610
 6611    cx.assert_editor_state(
 6612        &"
 6613            ˇ
 6614            ˇ]]
 6615            ˇ
 6616        "
 6617        .unindent(),
 6618    );
 6619}
 6620
 6621#[gpui::test]
 6622async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6623    init_test(cx, |_| {});
 6624
 6625    let language = Arc::new(Language::new(
 6626        LanguageConfig::default(),
 6627        Some(tree_sitter_rust::LANGUAGE.into()),
 6628    ));
 6629
 6630    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6631    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6632    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6633    editor
 6634        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6635        .await;
 6636
 6637    editor.update(cx, |editor, cx| {
 6638        editor.set_auto_replace_emoji_shortcode(true);
 6639
 6640        editor.handle_input("Hello ", cx);
 6641        editor.handle_input(":wave", cx);
 6642        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6643
 6644        editor.handle_input(":", cx);
 6645        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6646
 6647        editor.handle_input(" :smile", cx);
 6648        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6649
 6650        editor.handle_input(":", cx);
 6651        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6652
 6653        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6654        editor.handle_input(":wave", cx);
 6655        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6656
 6657        editor.handle_input(":", cx);
 6658        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6659
 6660        editor.handle_input(":1", cx);
 6661        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6662
 6663        editor.handle_input(":", cx);
 6664        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6665
 6666        // Ensure shortcode does not get replaced when it is part of a word
 6667        editor.handle_input(" Test:wave", cx);
 6668        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6669
 6670        editor.handle_input(":", cx);
 6671        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6672
 6673        editor.set_auto_replace_emoji_shortcode(false);
 6674
 6675        // Ensure shortcode does not get replaced when auto replace is off
 6676        editor.handle_input(" :wave", cx);
 6677        assert_eq!(
 6678            editor.text(cx),
 6679            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6680        );
 6681
 6682        editor.handle_input(":", cx);
 6683        assert_eq!(
 6684            editor.text(cx),
 6685            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6686        );
 6687    });
 6688}
 6689
 6690#[gpui::test]
 6691async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6692    init_test(cx, |_| {});
 6693
 6694    let (text, insertion_ranges) = marked_text_ranges(
 6695        indoc! {"
 6696            ˇ
 6697        "},
 6698        false,
 6699    );
 6700
 6701    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6702    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6703
 6704    _ = editor.update(cx, |editor, cx| {
 6705        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6706
 6707        editor
 6708            .insert_snippet(&insertion_ranges, snippet, cx)
 6709            .unwrap();
 6710
 6711        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6712            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6713            assert_eq!(editor.text(cx), expected_text);
 6714            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6715        }
 6716
 6717        assert(
 6718            editor,
 6719            cx,
 6720            indoc! {"
 6721            type «» =•
 6722            "},
 6723        );
 6724
 6725        assert!(editor.context_menu_visible(), "There should be a matches");
 6726    });
 6727}
 6728
 6729#[gpui::test]
 6730async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6731    init_test(cx, |_| {});
 6732
 6733    let (text, insertion_ranges) = marked_text_ranges(
 6734        indoc! {"
 6735            a.ˇ b
 6736            a.ˇ b
 6737            a.ˇ b
 6738        "},
 6739        false,
 6740    );
 6741
 6742    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6743    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6744
 6745    editor.update(cx, |editor, cx| {
 6746        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6747
 6748        editor
 6749            .insert_snippet(&insertion_ranges, snippet, cx)
 6750            .unwrap();
 6751
 6752        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6753            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6754            assert_eq!(editor.text(cx), expected_text);
 6755            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6756        }
 6757
 6758        assert(
 6759            editor,
 6760            cx,
 6761            indoc! {"
 6762                a.f(«one», two, «three») b
 6763                a.f(«one», two, «three») b
 6764                a.f(«one», two, «three») b
 6765            "},
 6766        );
 6767
 6768        // Can't move earlier than the first tab stop
 6769        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6770        assert(
 6771            editor,
 6772            cx,
 6773            indoc! {"
 6774                a.f(«one», two, «three») b
 6775                a.f(«one», two, «three») b
 6776                a.f(«one», two, «three») b
 6777            "},
 6778        );
 6779
 6780        assert!(editor.move_to_next_snippet_tabstop(cx));
 6781        assert(
 6782            editor,
 6783            cx,
 6784            indoc! {"
 6785                a.f(one, «two», three) b
 6786                a.f(one, «two», three) b
 6787                a.f(one, «two», three) b
 6788            "},
 6789        );
 6790
 6791        editor.move_to_prev_snippet_tabstop(cx);
 6792        assert(
 6793            editor,
 6794            cx,
 6795            indoc! {"
 6796                a.f(«one», two, «three») b
 6797                a.f(«one», two, «three») b
 6798                a.f(«one», two, «three») b
 6799            "},
 6800        );
 6801
 6802        assert!(editor.move_to_next_snippet_tabstop(cx));
 6803        assert(
 6804            editor,
 6805            cx,
 6806            indoc! {"
 6807                a.f(one, «two», three) b
 6808                a.f(one, «two», three) b
 6809                a.f(one, «two», three) b
 6810            "},
 6811        );
 6812        assert!(editor.move_to_next_snippet_tabstop(cx));
 6813        assert(
 6814            editor,
 6815            cx,
 6816            indoc! {"
 6817                a.f(one, two, three)ˇ b
 6818                a.f(one, two, three)ˇ b
 6819                a.f(one, two, three)ˇ b
 6820            "},
 6821        );
 6822
 6823        // As soon as the last tab stop is reached, snippet state is gone
 6824        editor.move_to_prev_snippet_tabstop(cx);
 6825        assert(
 6826            editor,
 6827            cx,
 6828            indoc! {"
 6829                a.f(one, two, three)ˇ b
 6830                a.f(one, two, three)ˇ b
 6831                a.f(one, two, three)ˇ b
 6832            "},
 6833        );
 6834    });
 6835}
 6836
 6837#[gpui::test]
 6838async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6839    init_test(cx, |_| {});
 6840
 6841    let fs = FakeFs::new(cx.executor());
 6842    fs.insert_file("/file.rs", Default::default()).await;
 6843
 6844    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6845
 6846    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6847    language_registry.add(rust_lang());
 6848    let mut fake_servers = language_registry.register_fake_lsp(
 6849        "Rust",
 6850        FakeLspAdapter {
 6851            capabilities: lsp::ServerCapabilities {
 6852                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6853                ..Default::default()
 6854            },
 6855            ..Default::default()
 6856        },
 6857    );
 6858
 6859    let buffer = project
 6860        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6861        .await
 6862        .unwrap();
 6863
 6864    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6865    let (editor, cx) =
 6866        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 6867    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6868    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6869
 6870    cx.executor().start_waiting();
 6871    let fake_server = fake_servers.next().await.unwrap();
 6872
 6873    let save = editor
 6874        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6875        .unwrap();
 6876    fake_server
 6877        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6878            assert_eq!(
 6879                params.text_document.uri,
 6880                lsp::Url::from_file_path("/file.rs").unwrap()
 6881            );
 6882            assert_eq!(params.options.tab_size, 4);
 6883            Ok(Some(vec![lsp::TextEdit::new(
 6884                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6885                ", ".to_string(),
 6886            )]))
 6887        })
 6888        .next()
 6889        .await;
 6890    cx.executor().start_waiting();
 6891    save.await;
 6892
 6893    assert_eq!(
 6894        editor.update(cx, |editor, cx| editor.text(cx)),
 6895        "one, two\nthree\n"
 6896    );
 6897    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6898
 6899    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6900    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6901
 6902    // Ensure we can still save even if formatting hangs.
 6903    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6904        assert_eq!(
 6905            params.text_document.uri,
 6906            lsp::Url::from_file_path("/file.rs").unwrap()
 6907        );
 6908        futures::future::pending::<()>().await;
 6909        unreachable!()
 6910    });
 6911    let save = editor
 6912        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6913        .unwrap();
 6914    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6915    cx.executor().start_waiting();
 6916    save.await;
 6917    assert_eq!(
 6918        editor.update(cx, |editor, cx| editor.text(cx)),
 6919        "one\ntwo\nthree\n"
 6920    );
 6921    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6922
 6923    // For non-dirty buffer, no formatting request should be sent
 6924    let save = editor
 6925        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6926        .unwrap();
 6927    let _pending_format_request = fake_server
 6928        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6929            panic!("Should not be invoked on non-dirty buffer");
 6930        })
 6931        .next();
 6932    cx.executor().start_waiting();
 6933    save.await;
 6934
 6935    // Set rust language override and assert overridden tabsize is sent to language server
 6936    update_test_language_settings(cx, |settings| {
 6937        settings.languages.insert(
 6938            "Rust".into(),
 6939            LanguageSettingsContent {
 6940                tab_size: NonZeroU32::new(8),
 6941                ..Default::default()
 6942            },
 6943        );
 6944    });
 6945
 6946    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6947    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6948    let save = editor
 6949        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6950        .unwrap();
 6951    fake_server
 6952        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6953            assert_eq!(
 6954                params.text_document.uri,
 6955                lsp::Url::from_file_path("/file.rs").unwrap()
 6956            );
 6957            assert_eq!(params.options.tab_size, 8);
 6958            Ok(Some(vec![]))
 6959        })
 6960        .next()
 6961        .await;
 6962    cx.executor().start_waiting();
 6963    save.await;
 6964}
 6965
 6966#[gpui::test]
 6967async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6968    init_test(cx, |_| {});
 6969
 6970    let cols = 4;
 6971    let rows = 10;
 6972    let sample_text_1 = sample_text(rows, cols, 'a');
 6973    assert_eq!(
 6974        sample_text_1,
 6975        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6976    );
 6977    let sample_text_2 = sample_text(rows, cols, 'l');
 6978    assert_eq!(
 6979        sample_text_2,
 6980        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6981    );
 6982    let sample_text_3 = sample_text(rows, cols, 'v');
 6983    assert_eq!(
 6984        sample_text_3,
 6985        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6986    );
 6987
 6988    let fs = FakeFs::new(cx.executor());
 6989    fs.insert_tree(
 6990        "/a",
 6991        json!({
 6992            "main.rs": sample_text_1,
 6993            "other.rs": sample_text_2,
 6994            "lib.rs": sample_text_3,
 6995        }),
 6996    )
 6997    .await;
 6998
 6999    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 7000    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7001    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7002
 7003    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7004    language_registry.add(rust_lang());
 7005    let mut fake_servers = language_registry.register_fake_lsp(
 7006        "Rust",
 7007        FakeLspAdapter {
 7008            capabilities: lsp::ServerCapabilities {
 7009                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7010                ..Default::default()
 7011            },
 7012            ..Default::default()
 7013        },
 7014    );
 7015
 7016    let worktree = project.update(cx, |project, cx| {
 7017        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7018        assert_eq!(worktrees.len(), 1);
 7019        worktrees.pop().unwrap()
 7020    });
 7021    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7022
 7023    let buffer_1 = project
 7024        .update(cx, |project, cx| {
 7025            project.open_buffer((worktree_id, "main.rs"), cx)
 7026        })
 7027        .await
 7028        .unwrap();
 7029    let buffer_2 = project
 7030        .update(cx, |project, cx| {
 7031            project.open_buffer((worktree_id, "other.rs"), cx)
 7032        })
 7033        .await
 7034        .unwrap();
 7035    let buffer_3 = project
 7036        .update(cx, |project, cx| {
 7037            project.open_buffer((worktree_id, "lib.rs"), cx)
 7038        })
 7039        .await
 7040        .unwrap();
 7041
 7042    let multi_buffer = cx.new_model(|cx| {
 7043        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7044        multi_buffer.push_excerpts(
 7045            buffer_1.clone(),
 7046            [
 7047                ExcerptRange {
 7048                    context: Point::new(0, 0)..Point::new(3, 0),
 7049                    primary: None,
 7050                },
 7051                ExcerptRange {
 7052                    context: Point::new(5, 0)..Point::new(7, 0),
 7053                    primary: None,
 7054                },
 7055                ExcerptRange {
 7056                    context: Point::new(9, 0)..Point::new(10, 4),
 7057                    primary: None,
 7058                },
 7059            ],
 7060            cx,
 7061        );
 7062        multi_buffer.push_excerpts(
 7063            buffer_2.clone(),
 7064            [
 7065                ExcerptRange {
 7066                    context: Point::new(0, 0)..Point::new(3, 0),
 7067                    primary: None,
 7068                },
 7069                ExcerptRange {
 7070                    context: Point::new(5, 0)..Point::new(7, 0),
 7071                    primary: None,
 7072                },
 7073                ExcerptRange {
 7074                    context: Point::new(9, 0)..Point::new(10, 4),
 7075                    primary: None,
 7076                },
 7077            ],
 7078            cx,
 7079        );
 7080        multi_buffer.push_excerpts(
 7081            buffer_3.clone(),
 7082            [
 7083                ExcerptRange {
 7084                    context: Point::new(0, 0)..Point::new(3, 0),
 7085                    primary: None,
 7086                },
 7087                ExcerptRange {
 7088                    context: Point::new(5, 0)..Point::new(7, 0),
 7089                    primary: None,
 7090                },
 7091                ExcerptRange {
 7092                    context: Point::new(9, 0)..Point::new(10, 4),
 7093                    primary: None,
 7094                },
 7095            ],
 7096            cx,
 7097        );
 7098        multi_buffer
 7099    });
 7100    let multi_buffer_editor = cx.new_view(|cx| {
 7101        Editor::new(
 7102            EditorMode::Full,
 7103            multi_buffer,
 7104            Some(project.clone()),
 7105            true,
 7106            cx,
 7107        )
 7108    });
 7109
 7110    multi_buffer_editor.update(cx, |editor, cx| {
 7111        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7112        editor.insert("|one|two|three|", cx);
 7113    });
 7114    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7115    multi_buffer_editor.update(cx, |editor, cx| {
 7116        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7117            s.select_ranges(Some(60..70))
 7118        });
 7119        editor.insert("|four|five|six|", cx);
 7120    });
 7121    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7122
 7123    // First two buffers should be edited, but not the third one.
 7124    assert_eq!(
 7125        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7126        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7127    );
 7128    buffer_1.update(cx, |buffer, _| {
 7129        assert!(buffer.is_dirty());
 7130        assert_eq!(
 7131            buffer.text(),
 7132            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7133        )
 7134    });
 7135    buffer_2.update(cx, |buffer, _| {
 7136        assert!(buffer.is_dirty());
 7137        assert_eq!(
 7138            buffer.text(),
 7139            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7140        )
 7141    });
 7142    buffer_3.update(cx, |buffer, _| {
 7143        assert!(!buffer.is_dirty());
 7144        assert_eq!(buffer.text(), sample_text_3,)
 7145    });
 7146    cx.executor().run_until_parked();
 7147
 7148    cx.executor().start_waiting();
 7149    let save = multi_buffer_editor
 7150        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7151        .unwrap();
 7152
 7153    let fake_server = fake_servers.next().await.unwrap();
 7154    fake_server
 7155        .server
 7156        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7157            Ok(Some(vec![lsp::TextEdit::new(
 7158                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7159                format!("[{} formatted]", params.text_document.uri),
 7160            )]))
 7161        })
 7162        .detach();
 7163    save.await;
 7164
 7165    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7166    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7167    assert_eq!(
 7168        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7169        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7170    );
 7171    buffer_1.update(cx, |buffer, _| {
 7172        assert!(!buffer.is_dirty());
 7173        assert_eq!(
 7174            buffer.text(),
 7175            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7176        )
 7177    });
 7178    buffer_2.update(cx, |buffer, _| {
 7179        assert!(!buffer.is_dirty());
 7180        assert_eq!(
 7181            buffer.text(),
 7182            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7183        )
 7184    });
 7185    buffer_3.update(cx, |buffer, _| {
 7186        assert!(!buffer.is_dirty());
 7187        assert_eq!(buffer.text(), sample_text_3,)
 7188    });
 7189}
 7190
 7191#[gpui::test]
 7192async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7193    init_test(cx, |_| {});
 7194
 7195    let fs = FakeFs::new(cx.executor());
 7196    fs.insert_file("/file.rs", Default::default()).await;
 7197
 7198    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7199
 7200    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7201    language_registry.add(rust_lang());
 7202    let mut fake_servers = language_registry.register_fake_lsp(
 7203        "Rust",
 7204        FakeLspAdapter {
 7205            capabilities: lsp::ServerCapabilities {
 7206                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7207                ..Default::default()
 7208            },
 7209            ..Default::default()
 7210        },
 7211    );
 7212
 7213    let buffer = project
 7214        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7215        .await
 7216        .unwrap();
 7217
 7218    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7219    let (editor, cx) =
 7220        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7221    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7222    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7223
 7224    cx.executor().start_waiting();
 7225    let fake_server = fake_servers.next().await.unwrap();
 7226
 7227    let save = editor
 7228        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7229        .unwrap();
 7230    fake_server
 7231        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7232            assert_eq!(
 7233                params.text_document.uri,
 7234                lsp::Url::from_file_path("/file.rs").unwrap()
 7235            );
 7236            assert_eq!(params.options.tab_size, 4);
 7237            Ok(Some(vec![lsp::TextEdit::new(
 7238                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7239                ", ".to_string(),
 7240            )]))
 7241        })
 7242        .next()
 7243        .await;
 7244    cx.executor().start_waiting();
 7245    save.await;
 7246    assert_eq!(
 7247        editor.update(cx, |editor, cx| editor.text(cx)),
 7248        "one, two\nthree\n"
 7249    );
 7250    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7251
 7252    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7253    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7254
 7255    // Ensure we can still save even if formatting hangs.
 7256    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7257        move |params, _| async move {
 7258            assert_eq!(
 7259                params.text_document.uri,
 7260                lsp::Url::from_file_path("/file.rs").unwrap()
 7261            );
 7262            futures::future::pending::<()>().await;
 7263            unreachable!()
 7264        },
 7265    );
 7266    let save = editor
 7267        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7268        .unwrap();
 7269    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7270    cx.executor().start_waiting();
 7271    save.await;
 7272    assert_eq!(
 7273        editor.update(cx, |editor, cx| editor.text(cx)),
 7274        "one\ntwo\nthree\n"
 7275    );
 7276    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7277
 7278    // For non-dirty buffer, no formatting request should be sent
 7279    let save = editor
 7280        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7281        .unwrap();
 7282    let _pending_format_request = fake_server
 7283        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7284            panic!("Should not be invoked on non-dirty buffer");
 7285        })
 7286        .next();
 7287    cx.executor().start_waiting();
 7288    save.await;
 7289
 7290    // Set Rust language override and assert overridden tabsize is sent to language server
 7291    update_test_language_settings(cx, |settings| {
 7292        settings.languages.insert(
 7293            "Rust".into(),
 7294            LanguageSettingsContent {
 7295                tab_size: NonZeroU32::new(8),
 7296                ..Default::default()
 7297            },
 7298        );
 7299    });
 7300
 7301    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7302    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7303    let save = editor
 7304        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7305        .unwrap();
 7306    fake_server
 7307        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7308            assert_eq!(
 7309                params.text_document.uri,
 7310                lsp::Url::from_file_path("/file.rs").unwrap()
 7311            );
 7312            assert_eq!(params.options.tab_size, 8);
 7313            Ok(Some(vec![]))
 7314        })
 7315        .next()
 7316        .await;
 7317    cx.executor().start_waiting();
 7318    save.await;
 7319}
 7320
 7321#[gpui::test]
 7322async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7323    init_test(cx, |settings| {
 7324        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7325            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7326        ))
 7327    });
 7328
 7329    let fs = FakeFs::new(cx.executor());
 7330    fs.insert_file("/file.rs", Default::default()).await;
 7331
 7332    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7333
 7334    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7335    language_registry.add(Arc::new(Language::new(
 7336        LanguageConfig {
 7337            name: "Rust".into(),
 7338            matcher: LanguageMatcher {
 7339                path_suffixes: vec!["rs".to_string()],
 7340                ..Default::default()
 7341            },
 7342            ..LanguageConfig::default()
 7343        },
 7344        Some(tree_sitter_rust::LANGUAGE.into()),
 7345    )));
 7346    update_test_language_settings(cx, |settings| {
 7347        // Enable Prettier formatting for the same buffer, and ensure
 7348        // LSP is called instead of Prettier.
 7349        settings.defaults.prettier = Some(PrettierSettings {
 7350            allowed: true,
 7351            ..PrettierSettings::default()
 7352        });
 7353    });
 7354    let mut fake_servers = language_registry.register_fake_lsp(
 7355        "Rust",
 7356        FakeLspAdapter {
 7357            capabilities: lsp::ServerCapabilities {
 7358                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7359                ..Default::default()
 7360            },
 7361            ..Default::default()
 7362        },
 7363    );
 7364
 7365    let buffer = project
 7366        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7367        .await
 7368        .unwrap();
 7369
 7370    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7371    let (editor, cx) =
 7372        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7373    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7374
 7375    cx.executor().start_waiting();
 7376    let fake_server = fake_servers.next().await.unwrap();
 7377
 7378    let format = editor
 7379        .update(cx, |editor, cx| {
 7380            editor.perform_format(
 7381                project.clone(),
 7382                FormatTrigger::Manual,
 7383                FormatTarget::Buffer,
 7384                cx,
 7385            )
 7386        })
 7387        .unwrap();
 7388    fake_server
 7389        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7390            assert_eq!(
 7391                params.text_document.uri,
 7392                lsp::Url::from_file_path("/file.rs").unwrap()
 7393            );
 7394            assert_eq!(params.options.tab_size, 4);
 7395            Ok(Some(vec![lsp::TextEdit::new(
 7396                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7397                ", ".to_string(),
 7398            )]))
 7399        })
 7400        .next()
 7401        .await;
 7402    cx.executor().start_waiting();
 7403    format.await;
 7404    assert_eq!(
 7405        editor.update(cx, |editor, cx| editor.text(cx)),
 7406        "one, two\nthree\n"
 7407    );
 7408
 7409    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7410    // Ensure we don't lock if formatting hangs.
 7411    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7412        assert_eq!(
 7413            params.text_document.uri,
 7414            lsp::Url::from_file_path("/file.rs").unwrap()
 7415        );
 7416        futures::future::pending::<()>().await;
 7417        unreachable!()
 7418    });
 7419    let format = editor
 7420        .update(cx, |editor, cx| {
 7421            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7422        })
 7423        .unwrap();
 7424    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7425    cx.executor().start_waiting();
 7426    format.await;
 7427    assert_eq!(
 7428        editor.update(cx, |editor, cx| editor.text(cx)),
 7429        "one\ntwo\nthree\n"
 7430    );
 7431}
 7432
 7433#[gpui::test]
 7434async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7435    init_test(cx, |_| {});
 7436
 7437    let mut cx = EditorLspTestContext::new_rust(
 7438        lsp::ServerCapabilities {
 7439            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7440            ..Default::default()
 7441        },
 7442        cx,
 7443    )
 7444    .await;
 7445
 7446    cx.set_state(indoc! {"
 7447        one.twoˇ
 7448    "});
 7449
 7450    // The format request takes a long time. When it completes, it inserts
 7451    // a newline and an indent before the `.`
 7452    cx.lsp
 7453        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7454            let executor = cx.background_executor().clone();
 7455            async move {
 7456                executor.timer(Duration::from_millis(100)).await;
 7457                Ok(Some(vec![lsp::TextEdit {
 7458                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7459                    new_text: "\n    ".into(),
 7460                }]))
 7461            }
 7462        });
 7463
 7464    // Submit a format request.
 7465    let format_1 = cx
 7466        .update_editor(|editor, cx| editor.format(&Format, cx))
 7467        .unwrap();
 7468    cx.executor().run_until_parked();
 7469
 7470    // Submit a second format request.
 7471    let format_2 = cx
 7472        .update_editor(|editor, cx| editor.format(&Format, cx))
 7473        .unwrap();
 7474    cx.executor().run_until_parked();
 7475
 7476    // Wait for both format requests to complete
 7477    cx.executor().advance_clock(Duration::from_millis(200));
 7478    cx.executor().start_waiting();
 7479    format_1.await.unwrap();
 7480    cx.executor().start_waiting();
 7481    format_2.await.unwrap();
 7482
 7483    // The formatting edits only happens once.
 7484    cx.assert_editor_state(indoc! {"
 7485        one
 7486            .twoˇ
 7487    "});
 7488}
 7489
 7490#[gpui::test]
 7491async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7492    init_test(cx, |settings| {
 7493        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7494    });
 7495
 7496    let mut cx = EditorLspTestContext::new_rust(
 7497        lsp::ServerCapabilities {
 7498            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7499            ..Default::default()
 7500        },
 7501        cx,
 7502    )
 7503    .await;
 7504
 7505    // Set up a buffer white some trailing whitespace and no trailing newline.
 7506    cx.set_state(
 7507        &[
 7508            "one ",   //
 7509            "twoˇ",   //
 7510            "three ", //
 7511            "four",   //
 7512        ]
 7513        .join("\n"),
 7514    );
 7515
 7516    // Submit a format request.
 7517    let format = cx
 7518        .update_editor(|editor, cx| editor.format(&Format, cx))
 7519        .unwrap();
 7520
 7521    // Record which buffer changes have been sent to the language server
 7522    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7523    cx.lsp
 7524        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7525            let buffer_changes = buffer_changes.clone();
 7526            move |params, _| {
 7527                buffer_changes.lock().extend(
 7528                    params
 7529                        .content_changes
 7530                        .into_iter()
 7531                        .map(|e| (e.range.unwrap(), e.text)),
 7532                );
 7533            }
 7534        });
 7535
 7536    // Handle formatting requests to the language server.
 7537    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7538        let buffer_changes = buffer_changes.clone();
 7539        move |_, _| {
 7540            // When formatting is requested, trailing whitespace has already been stripped,
 7541            // and the trailing newline has already been added.
 7542            assert_eq!(
 7543                &buffer_changes.lock()[1..],
 7544                &[
 7545                    (
 7546                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7547                        "".into()
 7548                    ),
 7549                    (
 7550                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7551                        "".into()
 7552                    ),
 7553                    (
 7554                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7555                        "\n".into()
 7556                    ),
 7557                ]
 7558            );
 7559
 7560            // Insert blank lines between each line of the buffer.
 7561            async move {
 7562                Ok(Some(vec![
 7563                    lsp::TextEdit {
 7564                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7565                        new_text: "\n".into(),
 7566                    },
 7567                    lsp::TextEdit {
 7568                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7569                        new_text: "\n".into(),
 7570                    },
 7571                ]))
 7572            }
 7573        }
 7574    });
 7575
 7576    // After formatting the buffer, the trailing whitespace is stripped,
 7577    // a newline is appended, and the edits provided by the language server
 7578    // have been applied.
 7579    format.await.unwrap();
 7580    cx.assert_editor_state(
 7581        &[
 7582            "one",   //
 7583            "",      //
 7584            "twoˇ",  //
 7585            "",      //
 7586            "three", //
 7587            "four",  //
 7588            "",      //
 7589        ]
 7590        .join("\n"),
 7591    );
 7592
 7593    // Undoing the formatting undoes the trailing whitespace removal, the
 7594    // trailing newline, and the LSP edits.
 7595    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7596    cx.assert_editor_state(
 7597        &[
 7598            "one ",   //
 7599            "twoˇ",   //
 7600            "three ", //
 7601            "four",   //
 7602        ]
 7603        .join("\n"),
 7604    );
 7605}
 7606
 7607#[gpui::test]
 7608async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7609    cx: &mut gpui::TestAppContext,
 7610) {
 7611    init_test(cx, |_| {});
 7612
 7613    cx.update(|cx| {
 7614        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7615            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7616                settings.auto_signature_help = Some(true);
 7617            });
 7618        });
 7619    });
 7620
 7621    let mut cx = EditorLspTestContext::new_rust(
 7622        lsp::ServerCapabilities {
 7623            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7624                ..Default::default()
 7625            }),
 7626            ..Default::default()
 7627        },
 7628        cx,
 7629    )
 7630    .await;
 7631
 7632    let language = Language::new(
 7633        LanguageConfig {
 7634            name: "Rust".into(),
 7635            brackets: BracketPairConfig {
 7636                pairs: vec![
 7637                    BracketPair {
 7638                        start: "{".to_string(),
 7639                        end: "}".to_string(),
 7640                        close: true,
 7641                        surround: true,
 7642                        newline: true,
 7643                    },
 7644                    BracketPair {
 7645                        start: "(".to_string(),
 7646                        end: ")".to_string(),
 7647                        close: true,
 7648                        surround: true,
 7649                        newline: true,
 7650                    },
 7651                    BracketPair {
 7652                        start: "/*".to_string(),
 7653                        end: " */".to_string(),
 7654                        close: true,
 7655                        surround: true,
 7656                        newline: true,
 7657                    },
 7658                    BracketPair {
 7659                        start: "[".to_string(),
 7660                        end: "]".to_string(),
 7661                        close: false,
 7662                        surround: false,
 7663                        newline: true,
 7664                    },
 7665                    BracketPair {
 7666                        start: "\"".to_string(),
 7667                        end: "\"".to_string(),
 7668                        close: true,
 7669                        surround: true,
 7670                        newline: false,
 7671                    },
 7672                    BracketPair {
 7673                        start: "<".to_string(),
 7674                        end: ">".to_string(),
 7675                        close: false,
 7676                        surround: true,
 7677                        newline: true,
 7678                    },
 7679                ],
 7680                ..Default::default()
 7681            },
 7682            autoclose_before: "})]".to_string(),
 7683            ..Default::default()
 7684        },
 7685        Some(tree_sitter_rust::LANGUAGE.into()),
 7686    );
 7687    let language = Arc::new(language);
 7688
 7689    cx.language_registry().add(language.clone());
 7690    cx.update_buffer(|buffer, cx| {
 7691        buffer.set_language(Some(language), cx);
 7692    });
 7693
 7694    cx.set_state(
 7695        &r#"
 7696            fn main() {
 7697                sampleˇ
 7698            }
 7699        "#
 7700        .unindent(),
 7701    );
 7702
 7703    cx.update_editor(|view, cx| {
 7704        view.handle_input("(", cx);
 7705    });
 7706    cx.assert_editor_state(
 7707        &"
 7708            fn main() {
 7709                sample(ˇ)
 7710            }
 7711        "
 7712        .unindent(),
 7713    );
 7714
 7715    let mocked_response = lsp::SignatureHelp {
 7716        signatures: vec![lsp::SignatureInformation {
 7717            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7718            documentation: None,
 7719            parameters: Some(vec![
 7720                lsp::ParameterInformation {
 7721                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7722                    documentation: None,
 7723                },
 7724                lsp::ParameterInformation {
 7725                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7726                    documentation: None,
 7727                },
 7728            ]),
 7729            active_parameter: None,
 7730        }],
 7731        active_signature: Some(0),
 7732        active_parameter: Some(0),
 7733    };
 7734    handle_signature_help_request(&mut cx, mocked_response).await;
 7735
 7736    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7737        .await;
 7738
 7739    cx.editor(|editor, _| {
 7740        let signature_help_state = editor.signature_help_state.popover().cloned();
 7741        assert!(signature_help_state.is_some());
 7742        let ParsedMarkdown {
 7743            text, highlights, ..
 7744        } = signature_help_state.unwrap().parsed_content;
 7745        assert_eq!(text, "param1: u8, param2: u8");
 7746        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7747    });
 7748}
 7749
 7750#[gpui::test]
 7751async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7752    init_test(cx, |_| {});
 7753
 7754    cx.update(|cx| {
 7755        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7756            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7757                settings.auto_signature_help = Some(false);
 7758                settings.show_signature_help_after_edits = Some(false);
 7759            });
 7760        });
 7761    });
 7762
 7763    let mut cx = EditorLspTestContext::new_rust(
 7764        lsp::ServerCapabilities {
 7765            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7766                ..Default::default()
 7767            }),
 7768            ..Default::default()
 7769        },
 7770        cx,
 7771    )
 7772    .await;
 7773
 7774    let language = Language::new(
 7775        LanguageConfig {
 7776            name: "Rust".into(),
 7777            brackets: BracketPairConfig {
 7778                pairs: vec![
 7779                    BracketPair {
 7780                        start: "{".to_string(),
 7781                        end: "}".to_string(),
 7782                        close: true,
 7783                        surround: true,
 7784                        newline: true,
 7785                    },
 7786                    BracketPair {
 7787                        start: "(".to_string(),
 7788                        end: ")".to_string(),
 7789                        close: true,
 7790                        surround: true,
 7791                        newline: true,
 7792                    },
 7793                    BracketPair {
 7794                        start: "/*".to_string(),
 7795                        end: " */".to_string(),
 7796                        close: true,
 7797                        surround: true,
 7798                        newline: true,
 7799                    },
 7800                    BracketPair {
 7801                        start: "[".to_string(),
 7802                        end: "]".to_string(),
 7803                        close: false,
 7804                        surround: false,
 7805                        newline: true,
 7806                    },
 7807                    BracketPair {
 7808                        start: "\"".to_string(),
 7809                        end: "\"".to_string(),
 7810                        close: true,
 7811                        surround: true,
 7812                        newline: false,
 7813                    },
 7814                    BracketPair {
 7815                        start: "<".to_string(),
 7816                        end: ">".to_string(),
 7817                        close: false,
 7818                        surround: true,
 7819                        newline: true,
 7820                    },
 7821                ],
 7822                ..Default::default()
 7823            },
 7824            autoclose_before: "})]".to_string(),
 7825            ..Default::default()
 7826        },
 7827        Some(tree_sitter_rust::LANGUAGE.into()),
 7828    );
 7829    let language = Arc::new(language);
 7830
 7831    cx.language_registry().add(language.clone());
 7832    cx.update_buffer(|buffer, cx| {
 7833        buffer.set_language(Some(language), cx);
 7834    });
 7835
 7836    // Ensure that signature_help is not called when no signature help is enabled.
 7837    cx.set_state(
 7838        &r#"
 7839            fn main() {
 7840                sampleˇ
 7841            }
 7842        "#
 7843        .unindent(),
 7844    );
 7845    cx.update_editor(|view, cx| {
 7846        view.handle_input("(", cx);
 7847    });
 7848    cx.assert_editor_state(
 7849        &"
 7850            fn main() {
 7851                sample(ˇ)
 7852            }
 7853        "
 7854        .unindent(),
 7855    );
 7856    cx.editor(|editor, _| {
 7857        assert!(editor.signature_help_state.task().is_none());
 7858    });
 7859
 7860    let mocked_response = lsp::SignatureHelp {
 7861        signatures: vec![lsp::SignatureInformation {
 7862            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7863            documentation: None,
 7864            parameters: Some(vec![
 7865                lsp::ParameterInformation {
 7866                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7867                    documentation: None,
 7868                },
 7869                lsp::ParameterInformation {
 7870                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7871                    documentation: None,
 7872                },
 7873            ]),
 7874            active_parameter: None,
 7875        }],
 7876        active_signature: Some(0),
 7877        active_parameter: Some(0),
 7878    };
 7879
 7880    // Ensure that signature_help is called when enabled afte edits
 7881    cx.update(|cx| {
 7882        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7883            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7884                settings.auto_signature_help = Some(false);
 7885                settings.show_signature_help_after_edits = Some(true);
 7886            });
 7887        });
 7888    });
 7889    cx.set_state(
 7890        &r#"
 7891            fn main() {
 7892                sampleˇ
 7893            }
 7894        "#
 7895        .unindent(),
 7896    );
 7897    cx.update_editor(|view, cx| {
 7898        view.handle_input("(", cx);
 7899    });
 7900    cx.assert_editor_state(
 7901        &"
 7902            fn main() {
 7903                sample(ˇ)
 7904            }
 7905        "
 7906        .unindent(),
 7907    );
 7908    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7909    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7910        .await;
 7911    cx.update_editor(|editor, _| {
 7912        let signature_help_state = editor.signature_help_state.popover().cloned();
 7913        assert!(signature_help_state.is_some());
 7914        let ParsedMarkdown {
 7915            text, highlights, ..
 7916        } = signature_help_state.unwrap().parsed_content;
 7917        assert_eq!(text, "param1: u8, param2: u8");
 7918        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7919        editor.signature_help_state = SignatureHelpState::default();
 7920    });
 7921
 7922    // Ensure that signature_help is called when auto signature help override is enabled
 7923    cx.update(|cx| {
 7924        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7925            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7926                settings.auto_signature_help = Some(true);
 7927                settings.show_signature_help_after_edits = Some(false);
 7928            });
 7929        });
 7930    });
 7931    cx.set_state(
 7932        &r#"
 7933            fn main() {
 7934                sampleˇ
 7935            }
 7936        "#
 7937        .unindent(),
 7938    );
 7939    cx.update_editor(|view, cx| {
 7940        view.handle_input("(", cx);
 7941    });
 7942    cx.assert_editor_state(
 7943        &"
 7944            fn main() {
 7945                sample(ˇ)
 7946            }
 7947        "
 7948        .unindent(),
 7949    );
 7950    handle_signature_help_request(&mut cx, mocked_response).await;
 7951    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7952        .await;
 7953    cx.editor(|editor, _| {
 7954        let signature_help_state = editor.signature_help_state.popover().cloned();
 7955        assert!(signature_help_state.is_some());
 7956        let ParsedMarkdown {
 7957            text, highlights, ..
 7958        } = signature_help_state.unwrap().parsed_content;
 7959        assert_eq!(text, "param1: u8, param2: u8");
 7960        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7961    });
 7962}
 7963
 7964#[gpui::test]
 7965async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7966    init_test(cx, |_| {});
 7967    cx.update(|cx| {
 7968        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7969            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7970                settings.auto_signature_help = Some(true);
 7971            });
 7972        });
 7973    });
 7974
 7975    let mut cx = EditorLspTestContext::new_rust(
 7976        lsp::ServerCapabilities {
 7977            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7978                ..Default::default()
 7979            }),
 7980            ..Default::default()
 7981        },
 7982        cx,
 7983    )
 7984    .await;
 7985
 7986    // A test that directly calls `show_signature_help`
 7987    cx.update_editor(|editor, cx| {
 7988        editor.show_signature_help(&ShowSignatureHelp, cx);
 7989    });
 7990
 7991    let mocked_response = lsp::SignatureHelp {
 7992        signatures: vec![lsp::SignatureInformation {
 7993            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7994            documentation: None,
 7995            parameters: Some(vec![
 7996                lsp::ParameterInformation {
 7997                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7998                    documentation: None,
 7999                },
 8000                lsp::ParameterInformation {
 8001                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8002                    documentation: None,
 8003                },
 8004            ]),
 8005            active_parameter: None,
 8006        }],
 8007        active_signature: Some(0),
 8008        active_parameter: Some(0),
 8009    };
 8010    handle_signature_help_request(&mut cx, mocked_response).await;
 8011
 8012    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8013        .await;
 8014
 8015    cx.editor(|editor, _| {
 8016        let signature_help_state = editor.signature_help_state.popover().cloned();
 8017        assert!(signature_help_state.is_some());
 8018        let ParsedMarkdown {
 8019            text, highlights, ..
 8020        } = signature_help_state.unwrap().parsed_content;
 8021        assert_eq!(text, "param1: u8, param2: u8");
 8022        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8023    });
 8024
 8025    // When exiting outside from inside the brackets, `signature_help` is closed.
 8026    cx.set_state(indoc! {"
 8027        fn main() {
 8028            sample(ˇ);
 8029        }
 8030
 8031        fn sample(param1: u8, param2: u8) {}
 8032    "});
 8033
 8034    cx.update_editor(|editor, cx| {
 8035        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8036    });
 8037
 8038    let mocked_response = lsp::SignatureHelp {
 8039        signatures: Vec::new(),
 8040        active_signature: None,
 8041        active_parameter: None,
 8042    };
 8043    handle_signature_help_request(&mut cx, mocked_response).await;
 8044
 8045    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8046        .await;
 8047
 8048    cx.editor(|editor, _| {
 8049        assert!(!editor.signature_help_state.is_shown());
 8050    });
 8051
 8052    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8053    cx.set_state(indoc! {"
 8054        fn main() {
 8055            sample(ˇ);
 8056        }
 8057
 8058        fn sample(param1: u8, param2: u8) {}
 8059    "});
 8060
 8061    let mocked_response = lsp::SignatureHelp {
 8062        signatures: vec![lsp::SignatureInformation {
 8063            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8064            documentation: None,
 8065            parameters: Some(vec![
 8066                lsp::ParameterInformation {
 8067                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8068                    documentation: None,
 8069                },
 8070                lsp::ParameterInformation {
 8071                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8072                    documentation: None,
 8073                },
 8074            ]),
 8075            active_parameter: None,
 8076        }],
 8077        active_signature: Some(0),
 8078        active_parameter: Some(0),
 8079    };
 8080    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8081    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8082        .await;
 8083    cx.editor(|editor, _| {
 8084        assert!(editor.signature_help_state.is_shown());
 8085    });
 8086
 8087    // Restore the popover with more parameter input
 8088    cx.set_state(indoc! {"
 8089        fn main() {
 8090            sample(param1, param2ˇ);
 8091        }
 8092
 8093        fn sample(param1: u8, param2: u8) {}
 8094    "});
 8095
 8096    let mocked_response = lsp::SignatureHelp {
 8097        signatures: vec![lsp::SignatureInformation {
 8098            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8099            documentation: None,
 8100            parameters: Some(vec![
 8101                lsp::ParameterInformation {
 8102                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8103                    documentation: None,
 8104                },
 8105                lsp::ParameterInformation {
 8106                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8107                    documentation: None,
 8108                },
 8109            ]),
 8110            active_parameter: None,
 8111        }],
 8112        active_signature: Some(0),
 8113        active_parameter: Some(1),
 8114    };
 8115    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8116    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8117        .await;
 8118
 8119    // When selecting a range, the popover is gone.
 8120    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8121    cx.update_editor(|editor, cx| {
 8122        editor.change_selections(None, cx, |s| {
 8123            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8124        })
 8125    });
 8126    cx.assert_editor_state(indoc! {"
 8127        fn main() {
 8128            sample(param1, «ˇparam2»);
 8129        }
 8130
 8131        fn sample(param1: u8, param2: u8) {}
 8132    "});
 8133    cx.editor(|editor, _| {
 8134        assert!(!editor.signature_help_state.is_shown());
 8135    });
 8136
 8137    // When unselecting again, the popover is back if within the brackets.
 8138    cx.update_editor(|editor, cx| {
 8139        editor.change_selections(None, cx, |s| {
 8140            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8141        })
 8142    });
 8143    cx.assert_editor_state(indoc! {"
 8144        fn main() {
 8145            sample(param1, ˇparam2);
 8146        }
 8147
 8148        fn sample(param1: u8, param2: u8) {}
 8149    "});
 8150    handle_signature_help_request(&mut cx, mocked_response).await;
 8151    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8152        .await;
 8153    cx.editor(|editor, _| {
 8154        assert!(editor.signature_help_state.is_shown());
 8155    });
 8156
 8157    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8158    cx.update_editor(|editor, cx| {
 8159        editor.change_selections(None, cx, |s| {
 8160            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8161            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8162        })
 8163    });
 8164    cx.assert_editor_state(indoc! {"
 8165        fn main() {
 8166            sample(param1, ˇparam2);
 8167        }
 8168
 8169        fn sample(param1: u8, param2: u8) {}
 8170    "});
 8171
 8172    let mocked_response = lsp::SignatureHelp {
 8173        signatures: vec![lsp::SignatureInformation {
 8174            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8175            documentation: None,
 8176            parameters: Some(vec![
 8177                lsp::ParameterInformation {
 8178                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8179                    documentation: None,
 8180                },
 8181                lsp::ParameterInformation {
 8182                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8183                    documentation: None,
 8184                },
 8185            ]),
 8186            active_parameter: None,
 8187        }],
 8188        active_signature: Some(0),
 8189        active_parameter: Some(1),
 8190    };
 8191    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8192    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8193        .await;
 8194    cx.update_editor(|editor, cx| {
 8195        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8196    });
 8197    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8198        .await;
 8199    cx.update_editor(|editor, cx| {
 8200        editor.change_selections(None, cx, |s| {
 8201            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8202        })
 8203    });
 8204    cx.assert_editor_state(indoc! {"
 8205        fn main() {
 8206            sample(param1, «ˇparam2»);
 8207        }
 8208
 8209        fn sample(param1: u8, param2: u8) {}
 8210    "});
 8211    cx.update_editor(|editor, cx| {
 8212        editor.change_selections(None, cx, |s| {
 8213            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8214        })
 8215    });
 8216    cx.assert_editor_state(indoc! {"
 8217        fn main() {
 8218            sample(param1, ˇparam2);
 8219        }
 8220
 8221        fn sample(param1: u8, param2: u8) {}
 8222    "});
 8223    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8224        .await;
 8225}
 8226
 8227#[gpui::test]
 8228async fn test_completion(cx: &mut gpui::TestAppContext) {
 8229    init_test(cx, |_| {});
 8230
 8231    let mut cx = EditorLspTestContext::new_rust(
 8232        lsp::ServerCapabilities {
 8233            completion_provider: Some(lsp::CompletionOptions {
 8234                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8235                resolve_provider: Some(true),
 8236                ..Default::default()
 8237            }),
 8238            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8239            ..Default::default()
 8240        },
 8241        cx,
 8242    )
 8243    .await;
 8244    let counter = Arc::new(AtomicUsize::new(0));
 8245
 8246    cx.set_state(indoc! {"
 8247        oneˇ
 8248        two
 8249        three
 8250    "});
 8251    cx.simulate_keystroke(".");
 8252    handle_completion_request(
 8253        &mut cx,
 8254        indoc! {"
 8255            one.|<>
 8256            two
 8257            three
 8258        "},
 8259        vec!["first_completion", "second_completion"],
 8260        counter.clone(),
 8261    )
 8262    .await;
 8263    cx.condition(|editor, _| editor.context_menu_visible())
 8264        .await;
 8265    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8266
 8267    let _handler = handle_signature_help_request(
 8268        &mut cx,
 8269        lsp::SignatureHelp {
 8270            signatures: vec![lsp::SignatureInformation {
 8271                label: "test signature".to_string(),
 8272                documentation: None,
 8273                parameters: Some(vec![lsp::ParameterInformation {
 8274                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8275                    documentation: None,
 8276                }]),
 8277                active_parameter: None,
 8278            }],
 8279            active_signature: None,
 8280            active_parameter: None,
 8281        },
 8282    );
 8283    cx.update_editor(|editor, cx| {
 8284        assert!(
 8285            !editor.signature_help_state.is_shown(),
 8286            "No signature help was called for"
 8287        );
 8288        editor.show_signature_help(&ShowSignatureHelp, cx);
 8289    });
 8290    cx.run_until_parked();
 8291    cx.update_editor(|editor, _| {
 8292        assert!(
 8293            !editor.signature_help_state.is_shown(),
 8294            "No signature help should be shown when completions menu is open"
 8295        );
 8296    });
 8297
 8298    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8299        editor.context_menu_next(&Default::default(), cx);
 8300        editor
 8301            .confirm_completion(&ConfirmCompletion::default(), cx)
 8302            .unwrap()
 8303    });
 8304    cx.assert_editor_state(indoc! {"
 8305        one.second_completionˇ
 8306        two
 8307        three
 8308    "});
 8309
 8310    handle_resolve_completion_request(
 8311        &mut cx,
 8312        Some(vec![
 8313            (
 8314                //This overlaps with the primary completion edit which is
 8315                //misbehavior from the LSP spec, test that we filter it out
 8316                indoc! {"
 8317                    one.second_ˇcompletion
 8318                    two
 8319                    threeˇ
 8320                "},
 8321                "overlapping additional edit",
 8322            ),
 8323            (
 8324                indoc! {"
 8325                    one.second_completion
 8326                    two
 8327                    threeˇ
 8328                "},
 8329                "\nadditional edit",
 8330            ),
 8331        ]),
 8332    )
 8333    .await;
 8334    apply_additional_edits.await.unwrap();
 8335    cx.assert_editor_state(indoc! {"
 8336        one.second_completionˇ
 8337        two
 8338        three
 8339        additional edit
 8340    "});
 8341
 8342    cx.set_state(indoc! {"
 8343        one.second_completion
 8344        twoˇ
 8345        threeˇ
 8346        additional edit
 8347    "});
 8348    cx.simulate_keystroke(" ");
 8349    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8350    cx.simulate_keystroke("s");
 8351    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8352
 8353    cx.assert_editor_state(indoc! {"
 8354        one.second_completion
 8355        two sˇ
 8356        three sˇ
 8357        additional edit
 8358    "});
 8359    handle_completion_request(
 8360        &mut cx,
 8361        indoc! {"
 8362            one.second_completion
 8363            two s
 8364            three <s|>
 8365            additional edit
 8366        "},
 8367        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8368        counter.clone(),
 8369    )
 8370    .await;
 8371    cx.condition(|editor, _| editor.context_menu_visible())
 8372        .await;
 8373    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8374
 8375    cx.simulate_keystroke("i");
 8376
 8377    handle_completion_request(
 8378        &mut cx,
 8379        indoc! {"
 8380            one.second_completion
 8381            two si
 8382            three <si|>
 8383            additional edit
 8384        "},
 8385        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8386        counter.clone(),
 8387    )
 8388    .await;
 8389    cx.condition(|editor, _| editor.context_menu_visible())
 8390        .await;
 8391    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8392
 8393    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8394        editor
 8395            .confirm_completion(&ConfirmCompletion::default(), cx)
 8396            .unwrap()
 8397    });
 8398    cx.assert_editor_state(indoc! {"
 8399        one.second_completion
 8400        two sixth_completionˇ
 8401        three sixth_completionˇ
 8402        additional edit
 8403    "});
 8404
 8405    handle_resolve_completion_request(&mut cx, None).await;
 8406    apply_additional_edits.await.unwrap();
 8407
 8408    update_test_language_settings(&mut cx, |settings| {
 8409        settings.defaults.show_completions_on_input = Some(false);
 8410    });
 8411    cx.set_state("editorˇ");
 8412    cx.simulate_keystroke(".");
 8413    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8414    cx.simulate_keystroke("c");
 8415    cx.simulate_keystroke("l");
 8416    cx.simulate_keystroke("o");
 8417    cx.assert_editor_state("editor.cloˇ");
 8418    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8419    cx.update_editor(|editor, cx| {
 8420        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8421    });
 8422    handle_completion_request(
 8423        &mut cx,
 8424        "editor.<clo|>",
 8425        vec!["close", "clobber"],
 8426        counter.clone(),
 8427    )
 8428    .await;
 8429    cx.condition(|editor, _| editor.context_menu_visible())
 8430        .await;
 8431    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8432
 8433    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8434        editor
 8435            .confirm_completion(&ConfirmCompletion::default(), cx)
 8436            .unwrap()
 8437    });
 8438    cx.assert_editor_state("editor.closeˇ");
 8439    handle_resolve_completion_request(&mut cx, None).await;
 8440    apply_additional_edits.await.unwrap();
 8441}
 8442
 8443#[gpui::test]
 8444async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8445    init_test(cx, |_| {});
 8446    let mut cx = EditorLspTestContext::new_rust(
 8447        lsp::ServerCapabilities {
 8448            completion_provider: Some(lsp::CompletionOptions {
 8449                trigger_characters: Some(vec![".".to_string()]),
 8450                ..Default::default()
 8451            }),
 8452            ..Default::default()
 8453        },
 8454        cx,
 8455    )
 8456    .await;
 8457    cx.lsp
 8458        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8459            Ok(Some(lsp::CompletionResponse::Array(vec![
 8460                lsp::CompletionItem {
 8461                    label: "first".into(),
 8462                    ..Default::default()
 8463                },
 8464                lsp::CompletionItem {
 8465                    label: "last".into(),
 8466                    ..Default::default()
 8467                },
 8468            ])))
 8469        });
 8470    cx.set_state("variableˇ");
 8471    cx.simulate_keystroke(".");
 8472    cx.executor().run_until_parked();
 8473
 8474    cx.update_editor(|editor, _| {
 8475        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8476        {
 8477            assert_eq!(completion_menu_entries(&menu.entries), &["first", "last"]);
 8478        } else {
 8479            panic!("expected completion menu to be open");
 8480        }
 8481    });
 8482
 8483    cx.update_editor(|editor, cx| {
 8484        editor.move_page_down(&MovePageDown::default(), cx);
 8485        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8486        {
 8487            assert!(
 8488                menu.selected_item == 1,
 8489                "expected PageDown to select the last item from the context menu"
 8490            );
 8491        } else {
 8492            panic!("expected completion menu to stay open after PageDown");
 8493        }
 8494    });
 8495
 8496    cx.update_editor(|editor, cx| {
 8497        editor.move_page_up(&MovePageUp::default(), cx);
 8498        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8499        {
 8500            assert!(
 8501                menu.selected_item == 0,
 8502                "expected PageUp to select the first item from the context menu"
 8503            );
 8504        } else {
 8505            panic!("expected completion menu to stay open after PageUp");
 8506        }
 8507    });
 8508}
 8509
 8510#[gpui::test]
 8511async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8512    init_test(cx, |_| {});
 8513    let mut cx = EditorLspTestContext::new_rust(
 8514        lsp::ServerCapabilities {
 8515            completion_provider: Some(lsp::CompletionOptions {
 8516                trigger_characters: Some(vec![".".to_string()]),
 8517                ..Default::default()
 8518            }),
 8519            ..Default::default()
 8520        },
 8521        cx,
 8522    )
 8523    .await;
 8524    cx.lsp
 8525        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8526            Ok(Some(lsp::CompletionResponse::Array(vec![
 8527                lsp::CompletionItem {
 8528                    label: "Range".into(),
 8529                    sort_text: Some("a".into()),
 8530                    ..Default::default()
 8531                },
 8532                lsp::CompletionItem {
 8533                    label: "r".into(),
 8534                    sort_text: Some("b".into()),
 8535                    ..Default::default()
 8536                },
 8537                lsp::CompletionItem {
 8538                    label: "ret".into(),
 8539                    sort_text: Some("c".into()),
 8540                    ..Default::default()
 8541                },
 8542                lsp::CompletionItem {
 8543                    label: "return".into(),
 8544                    sort_text: Some("d".into()),
 8545                    ..Default::default()
 8546                },
 8547                lsp::CompletionItem {
 8548                    label: "slice".into(),
 8549                    sort_text: Some("d".into()),
 8550                    ..Default::default()
 8551                },
 8552            ])))
 8553        });
 8554    cx.set_state("");
 8555    cx.executor().run_until_parked();
 8556    cx.update_editor(|editor, cx| {
 8557        editor.show_completions(
 8558            &ShowCompletions {
 8559                trigger: Some("r".into()),
 8560            },
 8561            cx,
 8562        );
 8563    });
 8564    cx.executor().run_until_parked();
 8565
 8566    cx.update_editor(|editor, _| {
 8567        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8568        {
 8569            assert_eq!(
 8570                completion_menu_entries(&menu.entries),
 8571                &["r", "ret", "Range", "return"]
 8572            );
 8573        } else {
 8574            panic!("expected completion menu to be open");
 8575        }
 8576    });
 8577}
 8578
 8579#[gpui::test]
 8580async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8581    init_test(cx, |_| {});
 8582
 8583    let mut cx = EditorLspTestContext::new_rust(
 8584        lsp::ServerCapabilities {
 8585            completion_provider: Some(lsp::CompletionOptions {
 8586                trigger_characters: Some(vec![".".to_string()]),
 8587                resolve_provider: Some(true),
 8588                ..Default::default()
 8589            }),
 8590            ..Default::default()
 8591        },
 8592        cx,
 8593    )
 8594    .await;
 8595
 8596    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8597    cx.simulate_keystroke(".");
 8598    let completion_item = lsp::CompletionItem {
 8599        label: "Some".into(),
 8600        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8601        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8602        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8603            kind: lsp::MarkupKind::Markdown,
 8604            value: "```rust\nSome(2)\n```".to_string(),
 8605        })),
 8606        deprecated: Some(false),
 8607        sort_text: Some("Some".to_string()),
 8608        filter_text: Some("Some".to_string()),
 8609        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8610        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8611            range: lsp::Range {
 8612                start: lsp::Position {
 8613                    line: 0,
 8614                    character: 22,
 8615                },
 8616                end: lsp::Position {
 8617                    line: 0,
 8618                    character: 22,
 8619                },
 8620            },
 8621            new_text: "Some(2)".to_string(),
 8622        })),
 8623        additional_text_edits: Some(vec![lsp::TextEdit {
 8624            range: lsp::Range {
 8625                start: lsp::Position {
 8626                    line: 0,
 8627                    character: 20,
 8628                },
 8629                end: lsp::Position {
 8630                    line: 0,
 8631                    character: 22,
 8632                },
 8633            },
 8634            new_text: "".to_string(),
 8635        }]),
 8636        ..Default::default()
 8637    };
 8638
 8639    let closure_completion_item = completion_item.clone();
 8640    let counter = Arc::new(AtomicUsize::new(0));
 8641    let counter_clone = counter.clone();
 8642    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8643        let task_completion_item = closure_completion_item.clone();
 8644        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8645        async move {
 8646            Ok(Some(lsp::CompletionResponse::Array(vec![
 8647                task_completion_item,
 8648            ])))
 8649        }
 8650    });
 8651
 8652    cx.condition(|editor, _| editor.context_menu_visible())
 8653        .await;
 8654    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8655    assert!(request.next().await.is_some());
 8656    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8657
 8658    cx.simulate_keystroke("S");
 8659    cx.simulate_keystroke("o");
 8660    cx.simulate_keystroke("m");
 8661    cx.condition(|editor, _| editor.context_menu_visible())
 8662        .await;
 8663    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8664    assert!(request.next().await.is_some());
 8665    assert!(request.next().await.is_some());
 8666    assert!(request.next().await.is_some());
 8667    request.close();
 8668    assert!(request.next().await.is_none());
 8669    assert_eq!(
 8670        counter.load(atomic::Ordering::Acquire),
 8671        4,
 8672        "With the completions menu open, only one LSP request should happen per input"
 8673    );
 8674}
 8675
 8676#[gpui::test]
 8677async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8678    init_test(cx, |_| {});
 8679    let mut cx = EditorTestContext::new(cx).await;
 8680    let language = Arc::new(Language::new(
 8681        LanguageConfig {
 8682            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8683            ..Default::default()
 8684        },
 8685        Some(tree_sitter_rust::LANGUAGE.into()),
 8686    ));
 8687    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8688
 8689    // If multiple selections intersect a line, the line is only toggled once.
 8690    cx.set_state(indoc! {"
 8691        fn a() {
 8692            «//b();
 8693            ˇ»// «c();
 8694            //ˇ»  d();
 8695        }
 8696    "});
 8697
 8698    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8699
 8700    cx.assert_editor_state(indoc! {"
 8701        fn a() {
 8702            «b();
 8703            c();
 8704            ˇ» d();
 8705        }
 8706    "});
 8707
 8708    // The comment prefix is inserted at the same column for every line in a
 8709    // selection.
 8710    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8711
 8712    cx.assert_editor_state(indoc! {"
 8713        fn a() {
 8714            // «b();
 8715            // c();
 8716            ˇ»//  d();
 8717        }
 8718    "});
 8719
 8720    // If a selection ends at the beginning of a line, that line is not toggled.
 8721    cx.set_selections_state(indoc! {"
 8722        fn a() {
 8723            // b();
 8724            «// c();
 8725        ˇ»    //  d();
 8726        }
 8727    "});
 8728
 8729    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8730
 8731    cx.assert_editor_state(indoc! {"
 8732        fn a() {
 8733            // b();
 8734            «c();
 8735        ˇ»    //  d();
 8736        }
 8737    "});
 8738
 8739    // If a selection span a single line and is empty, the line is toggled.
 8740    cx.set_state(indoc! {"
 8741        fn a() {
 8742            a();
 8743            b();
 8744        ˇ
 8745        }
 8746    "});
 8747
 8748    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8749
 8750    cx.assert_editor_state(indoc! {"
 8751        fn a() {
 8752            a();
 8753            b();
 8754        //•ˇ
 8755        }
 8756    "});
 8757
 8758    // If a selection span multiple lines, empty lines are not toggled.
 8759    cx.set_state(indoc! {"
 8760        fn a() {
 8761            «a();
 8762
 8763            c();ˇ»
 8764        }
 8765    "});
 8766
 8767    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8768
 8769    cx.assert_editor_state(indoc! {"
 8770        fn a() {
 8771            // «a();
 8772
 8773            // c();ˇ»
 8774        }
 8775    "});
 8776
 8777    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8778    cx.set_state(indoc! {"
 8779        fn a() {
 8780            «// a();
 8781            /// b();
 8782            //! c();ˇ»
 8783        }
 8784    "});
 8785
 8786    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8787
 8788    cx.assert_editor_state(indoc! {"
 8789        fn a() {
 8790            «a();
 8791            b();
 8792            c();ˇ»
 8793        }
 8794    "});
 8795}
 8796
 8797#[gpui::test]
 8798async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8799    init_test(cx, |_| {});
 8800    let mut cx = EditorTestContext::new(cx).await;
 8801    let language = Arc::new(Language::new(
 8802        LanguageConfig {
 8803            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8804            ..Default::default()
 8805        },
 8806        Some(tree_sitter_rust::LANGUAGE.into()),
 8807    ));
 8808    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8809
 8810    let toggle_comments = &ToggleComments {
 8811        advance_downwards: false,
 8812        ignore_indent: true,
 8813    };
 8814
 8815    // If multiple selections intersect a line, the line is only toggled once.
 8816    cx.set_state(indoc! {"
 8817        fn a() {
 8818        //    «b();
 8819        //    c();
 8820        //    ˇ» d();
 8821        }
 8822    "});
 8823
 8824    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8825
 8826    cx.assert_editor_state(indoc! {"
 8827        fn a() {
 8828            «b();
 8829            c();
 8830            ˇ» d();
 8831        }
 8832    "});
 8833
 8834    // The comment prefix is inserted at the beginning of each line
 8835    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8836
 8837    cx.assert_editor_state(indoc! {"
 8838        fn a() {
 8839        //    «b();
 8840        //    c();
 8841        //    ˇ» d();
 8842        }
 8843    "});
 8844
 8845    // If a selection ends at the beginning of a line, that line is not toggled.
 8846    cx.set_selections_state(indoc! {"
 8847        fn a() {
 8848        //    b();
 8849        //    «c();
 8850        ˇ»//     d();
 8851        }
 8852    "});
 8853
 8854    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8855
 8856    cx.assert_editor_state(indoc! {"
 8857        fn a() {
 8858        //    b();
 8859            «c();
 8860        ˇ»//     d();
 8861        }
 8862    "});
 8863
 8864    // If a selection span a single line and is empty, the line is toggled.
 8865    cx.set_state(indoc! {"
 8866        fn a() {
 8867            a();
 8868            b();
 8869        ˇ
 8870        }
 8871    "});
 8872
 8873    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8874
 8875    cx.assert_editor_state(indoc! {"
 8876        fn a() {
 8877            a();
 8878            b();
 8879        //ˇ
 8880        }
 8881    "});
 8882
 8883    // If a selection span multiple lines, empty lines are not toggled.
 8884    cx.set_state(indoc! {"
 8885        fn a() {
 8886            «a();
 8887
 8888            c();ˇ»
 8889        }
 8890    "});
 8891
 8892    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8893
 8894    cx.assert_editor_state(indoc! {"
 8895        fn a() {
 8896        //    «a();
 8897
 8898        //    c();ˇ»
 8899        }
 8900    "});
 8901
 8902    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8903    cx.set_state(indoc! {"
 8904        fn a() {
 8905        //    «a();
 8906        ///    b();
 8907        //!    c();ˇ»
 8908        }
 8909    "});
 8910
 8911    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8912
 8913    cx.assert_editor_state(indoc! {"
 8914        fn a() {
 8915            «a();
 8916            b();
 8917            c();ˇ»
 8918        }
 8919    "});
 8920}
 8921
 8922#[gpui::test]
 8923async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8924    init_test(cx, |_| {});
 8925
 8926    let language = Arc::new(Language::new(
 8927        LanguageConfig {
 8928            line_comments: vec!["// ".into()],
 8929            ..Default::default()
 8930        },
 8931        Some(tree_sitter_rust::LANGUAGE.into()),
 8932    ));
 8933
 8934    let mut cx = EditorTestContext::new(cx).await;
 8935
 8936    cx.language_registry().add(language.clone());
 8937    cx.update_buffer(|buffer, cx| {
 8938        buffer.set_language(Some(language), cx);
 8939    });
 8940
 8941    let toggle_comments = &ToggleComments {
 8942        advance_downwards: true,
 8943        ignore_indent: false,
 8944    };
 8945
 8946    // Single cursor on one line -> advance
 8947    // Cursor moves horizontally 3 characters as well on non-blank line
 8948    cx.set_state(indoc!(
 8949        "fn a() {
 8950             ˇdog();
 8951             cat();
 8952        }"
 8953    ));
 8954    cx.update_editor(|editor, cx| {
 8955        editor.toggle_comments(toggle_comments, cx);
 8956    });
 8957    cx.assert_editor_state(indoc!(
 8958        "fn a() {
 8959             // dog();
 8960             catˇ();
 8961        }"
 8962    ));
 8963
 8964    // Single selection on one line -> don't advance
 8965    cx.set_state(indoc!(
 8966        "fn a() {
 8967             «dog()ˇ»;
 8968             cat();
 8969        }"
 8970    ));
 8971    cx.update_editor(|editor, cx| {
 8972        editor.toggle_comments(toggle_comments, cx);
 8973    });
 8974    cx.assert_editor_state(indoc!(
 8975        "fn a() {
 8976             // «dog()ˇ»;
 8977             cat();
 8978        }"
 8979    ));
 8980
 8981    // Multiple cursors on one line -> advance
 8982    cx.set_state(indoc!(
 8983        "fn a() {
 8984             ˇdˇog();
 8985             cat();
 8986        }"
 8987    ));
 8988    cx.update_editor(|editor, cx| {
 8989        editor.toggle_comments(toggle_comments, cx);
 8990    });
 8991    cx.assert_editor_state(indoc!(
 8992        "fn a() {
 8993             // dog();
 8994             catˇ(ˇ);
 8995        }"
 8996    ));
 8997
 8998    // Multiple cursors on one line, with selection -> don't advance
 8999    cx.set_state(indoc!(
 9000        "fn a() {
 9001             ˇdˇog«()ˇ»;
 9002             cat();
 9003        }"
 9004    ));
 9005    cx.update_editor(|editor, cx| {
 9006        editor.toggle_comments(toggle_comments, cx);
 9007    });
 9008    cx.assert_editor_state(indoc!(
 9009        "fn a() {
 9010             // ˇdˇog«()ˇ»;
 9011             cat();
 9012        }"
 9013    ));
 9014
 9015    // Single cursor on one line -> advance
 9016    // Cursor moves to column 0 on blank line
 9017    cx.set_state(indoc!(
 9018        "fn a() {
 9019             ˇdog();
 9020
 9021             cat();
 9022        }"
 9023    ));
 9024    cx.update_editor(|editor, cx| {
 9025        editor.toggle_comments(toggle_comments, cx);
 9026    });
 9027    cx.assert_editor_state(indoc!(
 9028        "fn a() {
 9029             // dog();
 9030        ˇ
 9031             cat();
 9032        }"
 9033    ));
 9034
 9035    // Single cursor on one line -> advance
 9036    // Cursor starts and ends at column 0
 9037    cx.set_state(indoc!(
 9038        "fn a() {
 9039         ˇ    dog();
 9040             cat();
 9041        }"
 9042    ));
 9043    cx.update_editor(|editor, cx| {
 9044        editor.toggle_comments(toggle_comments, cx);
 9045    });
 9046    cx.assert_editor_state(indoc!(
 9047        "fn a() {
 9048             // dog();
 9049         ˇ    cat();
 9050        }"
 9051    ));
 9052}
 9053
 9054#[gpui::test]
 9055async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9056    init_test(cx, |_| {});
 9057
 9058    let mut cx = EditorTestContext::new(cx).await;
 9059
 9060    let html_language = Arc::new(
 9061        Language::new(
 9062            LanguageConfig {
 9063                name: "HTML".into(),
 9064                block_comment: Some(("<!-- ".into(), " -->".into())),
 9065                ..Default::default()
 9066            },
 9067            Some(tree_sitter_html::language()),
 9068        )
 9069        .with_injection_query(
 9070            r#"
 9071            (script_element
 9072                (raw_text) @content
 9073                (#set! "language" "javascript"))
 9074            "#,
 9075        )
 9076        .unwrap(),
 9077    );
 9078
 9079    let javascript_language = Arc::new(Language::new(
 9080        LanguageConfig {
 9081            name: "JavaScript".into(),
 9082            line_comments: vec!["// ".into()],
 9083            ..Default::default()
 9084        },
 9085        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9086    ));
 9087
 9088    cx.language_registry().add(html_language.clone());
 9089    cx.language_registry().add(javascript_language.clone());
 9090    cx.update_buffer(|buffer, cx| {
 9091        buffer.set_language(Some(html_language), cx);
 9092    });
 9093
 9094    // Toggle comments for empty selections
 9095    cx.set_state(
 9096        &r#"
 9097            <p>A</p>ˇ
 9098            <p>B</p>ˇ
 9099            <p>C</p>ˇ
 9100        "#
 9101        .unindent(),
 9102    );
 9103    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9104    cx.assert_editor_state(
 9105        &r#"
 9106            <!-- <p>A</p>ˇ -->
 9107            <!-- <p>B</p>ˇ -->
 9108            <!-- <p>C</p>ˇ -->
 9109        "#
 9110        .unindent(),
 9111    );
 9112    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9113    cx.assert_editor_state(
 9114        &r#"
 9115            <p>A</p>ˇ
 9116            <p>B</p>ˇ
 9117            <p>C</p>ˇ
 9118        "#
 9119        .unindent(),
 9120    );
 9121
 9122    // Toggle comments for mixture of empty and non-empty selections, where
 9123    // multiple selections occupy a given line.
 9124    cx.set_state(
 9125        &r#"
 9126            <p>A«</p>
 9127            <p>ˇ»B</p>ˇ
 9128            <p>C«</p>
 9129            <p>ˇ»D</p>ˇ
 9130        "#
 9131        .unindent(),
 9132    );
 9133
 9134    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9135    cx.assert_editor_state(
 9136        &r#"
 9137            <!-- <p>A«</p>
 9138            <p>ˇ»B</p>ˇ -->
 9139            <!-- <p>C«</p>
 9140            <p>ˇ»D</p>ˇ -->
 9141        "#
 9142        .unindent(),
 9143    );
 9144    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9145    cx.assert_editor_state(
 9146        &r#"
 9147            <p>A«</p>
 9148            <p>ˇ»B</p>ˇ
 9149            <p>C«</p>
 9150            <p>ˇ»D</p>ˇ
 9151        "#
 9152        .unindent(),
 9153    );
 9154
 9155    // Toggle comments when different languages are active for different
 9156    // selections.
 9157    cx.set_state(
 9158        &r#"
 9159            ˇ<script>
 9160                ˇvar x = new Y();
 9161            ˇ</script>
 9162        "#
 9163        .unindent(),
 9164    );
 9165    cx.executor().run_until_parked();
 9166    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9167    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9168    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9169    cx.assert_editor_state(
 9170        &r#"
 9171            <!-- ˇ<script> -->
 9172                // ˇvar x = new Y();
 9173            // ˇ</script>
 9174        "#
 9175        .unindent(),
 9176    );
 9177}
 9178
 9179#[gpui::test]
 9180fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9181    init_test(cx, |_| {});
 9182
 9183    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9184    let multibuffer = cx.new_model(|cx| {
 9185        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9186        multibuffer.push_excerpts(
 9187            buffer.clone(),
 9188            [
 9189                ExcerptRange {
 9190                    context: Point::new(0, 0)..Point::new(0, 4),
 9191                    primary: None,
 9192                },
 9193                ExcerptRange {
 9194                    context: Point::new(1, 0)..Point::new(1, 4),
 9195                    primary: None,
 9196                },
 9197            ],
 9198            cx,
 9199        );
 9200        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9201        multibuffer
 9202    });
 9203
 9204    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9205    view.update(cx, |view, cx| {
 9206        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9207        view.change_selections(None, cx, |s| {
 9208            s.select_ranges([
 9209                Point::new(0, 0)..Point::new(0, 0),
 9210                Point::new(1, 0)..Point::new(1, 0),
 9211            ])
 9212        });
 9213
 9214        view.handle_input("X", cx);
 9215        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9216        assert_eq!(
 9217            view.selections.ranges(cx),
 9218            [
 9219                Point::new(0, 1)..Point::new(0, 1),
 9220                Point::new(1, 1)..Point::new(1, 1),
 9221            ]
 9222        );
 9223
 9224        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9225        view.change_selections(None, cx, |s| {
 9226            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9227        });
 9228        view.backspace(&Default::default(), cx);
 9229        assert_eq!(view.text(cx), "Xa\nbbb");
 9230        assert_eq!(
 9231            view.selections.ranges(cx),
 9232            [Point::new(1, 0)..Point::new(1, 0)]
 9233        );
 9234
 9235        view.change_selections(None, cx, |s| {
 9236            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9237        });
 9238        view.backspace(&Default::default(), cx);
 9239        assert_eq!(view.text(cx), "X\nbb");
 9240        assert_eq!(
 9241            view.selections.ranges(cx),
 9242            [Point::new(0, 1)..Point::new(0, 1)]
 9243        );
 9244    });
 9245}
 9246
 9247#[gpui::test]
 9248fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9249    init_test(cx, |_| {});
 9250
 9251    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9252    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9253        indoc! {"
 9254            [aaaa
 9255            (bbbb]
 9256            cccc)",
 9257        },
 9258        markers.clone(),
 9259    );
 9260    let excerpt_ranges = markers.into_iter().map(|marker| {
 9261        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9262        ExcerptRange {
 9263            context,
 9264            primary: None,
 9265        }
 9266    });
 9267    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9268    let multibuffer = cx.new_model(|cx| {
 9269        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9270        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9271        multibuffer
 9272    });
 9273
 9274    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9275    view.update(cx, |view, cx| {
 9276        let (expected_text, selection_ranges) = marked_text_ranges(
 9277            indoc! {"
 9278                aaaa
 9279                bˇbbb
 9280                bˇbbˇb
 9281                cccc"
 9282            },
 9283            true,
 9284        );
 9285        assert_eq!(view.text(cx), expected_text);
 9286        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9287
 9288        view.handle_input("X", cx);
 9289
 9290        let (expected_text, expected_selections) = marked_text_ranges(
 9291            indoc! {"
 9292                aaaa
 9293                bXˇbbXb
 9294                bXˇbbXˇb
 9295                cccc"
 9296            },
 9297            false,
 9298        );
 9299        assert_eq!(view.text(cx), expected_text);
 9300        assert_eq!(view.selections.ranges(cx), expected_selections);
 9301
 9302        view.newline(&Newline, cx);
 9303        let (expected_text, expected_selections) = marked_text_ranges(
 9304            indoc! {"
 9305                aaaa
 9306                bX
 9307                ˇbbX
 9308                b
 9309                bX
 9310                ˇbbX
 9311                ˇb
 9312                cccc"
 9313            },
 9314            false,
 9315        );
 9316        assert_eq!(view.text(cx), expected_text);
 9317        assert_eq!(view.selections.ranges(cx), expected_selections);
 9318    });
 9319}
 9320
 9321#[gpui::test]
 9322fn test_refresh_selections(cx: &mut TestAppContext) {
 9323    init_test(cx, |_| {});
 9324
 9325    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9326    let mut excerpt1_id = None;
 9327    let multibuffer = cx.new_model(|cx| {
 9328        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9329        excerpt1_id = multibuffer
 9330            .push_excerpts(
 9331                buffer.clone(),
 9332                [
 9333                    ExcerptRange {
 9334                        context: Point::new(0, 0)..Point::new(1, 4),
 9335                        primary: None,
 9336                    },
 9337                    ExcerptRange {
 9338                        context: Point::new(1, 0)..Point::new(2, 4),
 9339                        primary: None,
 9340                    },
 9341                ],
 9342                cx,
 9343            )
 9344            .into_iter()
 9345            .next();
 9346        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9347        multibuffer
 9348    });
 9349
 9350    let editor = cx.add_window(|cx| {
 9351        let mut editor = build_editor(multibuffer.clone(), cx);
 9352        let snapshot = editor.snapshot(cx);
 9353        editor.change_selections(None, cx, |s| {
 9354            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9355        });
 9356        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9357        assert_eq!(
 9358            editor.selections.ranges(cx),
 9359            [
 9360                Point::new(1, 3)..Point::new(1, 3),
 9361                Point::new(2, 1)..Point::new(2, 1),
 9362            ]
 9363        );
 9364        editor
 9365    });
 9366
 9367    // Refreshing selections is a no-op when excerpts haven't changed.
 9368    _ = editor.update(cx, |editor, cx| {
 9369        editor.change_selections(None, cx, |s| s.refresh());
 9370        assert_eq!(
 9371            editor.selections.ranges(cx),
 9372            [
 9373                Point::new(1, 3)..Point::new(1, 3),
 9374                Point::new(2, 1)..Point::new(2, 1),
 9375            ]
 9376        );
 9377    });
 9378
 9379    multibuffer.update(cx, |multibuffer, cx| {
 9380        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9381    });
 9382    _ = editor.update(cx, |editor, cx| {
 9383        // Removing an excerpt causes the first selection to become degenerate.
 9384        assert_eq!(
 9385            editor.selections.ranges(cx),
 9386            [
 9387                Point::new(0, 0)..Point::new(0, 0),
 9388                Point::new(0, 1)..Point::new(0, 1)
 9389            ]
 9390        );
 9391
 9392        // Refreshing selections will relocate the first selection to the original buffer
 9393        // location.
 9394        editor.change_selections(None, cx, |s| s.refresh());
 9395        assert_eq!(
 9396            editor.selections.ranges(cx),
 9397            [
 9398                Point::new(0, 1)..Point::new(0, 1),
 9399                Point::new(0, 3)..Point::new(0, 3)
 9400            ]
 9401        );
 9402        assert!(editor.selections.pending_anchor().is_some());
 9403    });
 9404}
 9405
 9406#[gpui::test]
 9407fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9408    init_test(cx, |_| {});
 9409
 9410    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9411    let mut excerpt1_id = None;
 9412    let multibuffer = cx.new_model(|cx| {
 9413        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9414        excerpt1_id = multibuffer
 9415            .push_excerpts(
 9416                buffer.clone(),
 9417                [
 9418                    ExcerptRange {
 9419                        context: Point::new(0, 0)..Point::new(1, 4),
 9420                        primary: None,
 9421                    },
 9422                    ExcerptRange {
 9423                        context: Point::new(1, 0)..Point::new(2, 4),
 9424                        primary: None,
 9425                    },
 9426                ],
 9427                cx,
 9428            )
 9429            .into_iter()
 9430            .next();
 9431        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9432        multibuffer
 9433    });
 9434
 9435    let editor = cx.add_window(|cx| {
 9436        let mut editor = build_editor(multibuffer.clone(), cx);
 9437        let snapshot = editor.snapshot(cx);
 9438        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9439        assert_eq!(
 9440            editor.selections.ranges(cx),
 9441            [Point::new(1, 3)..Point::new(1, 3)]
 9442        );
 9443        editor
 9444    });
 9445
 9446    multibuffer.update(cx, |multibuffer, cx| {
 9447        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9448    });
 9449    _ = editor.update(cx, |editor, cx| {
 9450        assert_eq!(
 9451            editor.selections.ranges(cx),
 9452            [Point::new(0, 0)..Point::new(0, 0)]
 9453        );
 9454
 9455        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9456        editor.change_selections(None, cx, |s| s.refresh());
 9457        assert_eq!(
 9458            editor.selections.ranges(cx),
 9459            [Point::new(0, 3)..Point::new(0, 3)]
 9460        );
 9461        assert!(editor.selections.pending_anchor().is_some());
 9462    });
 9463}
 9464
 9465#[gpui::test]
 9466async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9467    init_test(cx, |_| {});
 9468
 9469    let language = Arc::new(
 9470        Language::new(
 9471            LanguageConfig {
 9472                brackets: BracketPairConfig {
 9473                    pairs: vec![
 9474                        BracketPair {
 9475                            start: "{".to_string(),
 9476                            end: "}".to_string(),
 9477                            close: true,
 9478                            surround: true,
 9479                            newline: true,
 9480                        },
 9481                        BracketPair {
 9482                            start: "/* ".to_string(),
 9483                            end: " */".to_string(),
 9484                            close: true,
 9485                            surround: true,
 9486                            newline: true,
 9487                        },
 9488                    ],
 9489                    ..Default::default()
 9490                },
 9491                ..Default::default()
 9492            },
 9493            Some(tree_sitter_rust::LANGUAGE.into()),
 9494        )
 9495        .with_indents_query("")
 9496        .unwrap(),
 9497    );
 9498
 9499    let text = concat!(
 9500        "{   }\n",     //
 9501        "  x\n",       //
 9502        "  /*   */\n", //
 9503        "x\n",         //
 9504        "{{} }\n",     //
 9505    );
 9506
 9507    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9508    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9509    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9510    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9511        .await;
 9512
 9513    view.update(cx, |view, cx| {
 9514        view.change_selections(None, cx, |s| {
 9515            s.select_display_ranges([
 9516                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9517                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9518                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9519            ])
 9520        });
 9521        view.newline(&Newline, cx);
 9522
 9523        assert_eq!(
 9524            view.buffer().read(cx).read(cx).text(),
 9525            concat!(
 9526                "{ \n",    // Suppress rustfmt
 9527                "\n",      //
 9528                "}\n",     //
 9529                "  x\n",   //
 9530                "  /* \n", //
 9531                "  \n",    //
 9532                "  */\n",  //
 9533                "x\n",     //
 9534                "{{} \n",  //
 9535                "}\n",     //
 9536            )
 9537        );
 9538    });
 9539}
 9540
 9541#[gpui::test]
 9542fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9543    init_test(cx, |_| {});
 9544
 9545    let editor = cx.add_window(|cx| {
 9546        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9547        build_editor(buffer.clone(), cx)
 9548    });
 9549
 9550    _ = editor.update(cx, |editor, cx| {
 9551        struct Type1;
 9552        struct Type2;
 9553
 9554        let buffer = editor.buffer.read(cx).snapshot(cx);
 9555
 9556        let anchor_range =
 9557            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9558
 9559        editor.highlight_background::<Type1>(
 9560            &[
 9561                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9562                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9563                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9564                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9565            ],
 9566            |_| Hsla::red(),
 9567            cx,
 9568        );
 9569        editor.highlight_background::<Type2>(
 9570            &[
 9571                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9572                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9573                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9574                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9575            ],
 9576            |_| Hsla::green(),
 9577            cx,
 9578        );
 9579
 9580        let snapshot = editor.snapshot(cx);
 9581        let mut highlighted_ranges = editor.background_highlights_in_range(
 9582            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9583            &snapshot,
 9584            cx.theme().colors(),
 9585        );
 9586        // Enforce a consistent ordering based on color without relying on the ordering of the
 9587        // highlight's `TypeId` which is non-executor.
 9588        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9589        assert_eq!(
 9590            highlighted_ranges,
 9591            &[
 9592                (
 9593                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9594                    Hsla::red(),
 9595                ),
 9596                (
 9597                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9598                    Hsla::red(),
 9599                ),
 9600                (
 9601                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9602                    Hsla::green(),
 9603                ),
 9604                (
 9605                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9606                    Hsla::green(),
 9607                ),
 9608            ]
 9609        );
 9610        assert_eq!(
 9611            editor.background_highlights_in_range(
 9612                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9613                &snapshot,
 9614                cx.theme().colors(),
 9615            ),
 9616            &[(
 9617                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9618                Hsla::red(),
 9619            )]
 9620        );
 9621    });
 9622}
 9623
 9624#[gpui::test]
 9625async fn test_following(cx: &mut gpui::TestAppContext) {
 9626    init_test(cx, |_| {});
 9627
 9628    let fs = FakeFs::new(cx.executor());
 9629    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9630
 9631    let buffer = project.update(cx, |project, cx| {
 9632        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9633        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9634    });
 9635    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9636    let follower = cx.update(|cx| {
 9637        cx.open_window(
 9638            WindowOptions {
 9639                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9640                    gpui::Point::new(px(0.), px(0.)),
 9641                    gpui::Point::new(px(10.), px(80.)),
 9642                ))),
 9643                ..Default::default()
 9644            },
 9645            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9646        )
 9647        .unwrap()
 9648    });
 9649
 9650    let is_still_following = Rc::new(RefCell::new(true));
 9651    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9652    let pending_update = Rc::new(RefCell::new(None));
 9653    _ = follower.update(cx, {
 9654        let update = pending_update.clone();
 9655        let is_still_following = is_still_following.clone();
 9656        let follower_edit_event_count = follower_edit_event_count.clone();
 9657        |_, cx| {
 9658            cx.subscribe(
 9659                &leader.root_view(cx).unwrap(),
 9660                move |_, leader, event, cx| {
 9661                    leader
 9662                        .read(cx)
 9663                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9664                },
 9665            )
 9666            .detach();
 9667
 9668            cx.subscribe(
 9669                &follower.root_view(cx).unwrap(),
 9670                move |_, _, event: &EditorEvent, _cx| {
 9671                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9672                        *is_still_following.borrow_mut() = false;
 9673                    }
 9674
 9675                    if let EditorEvent::BufferEdited = event {
 9676                        *follower_edit_event_count.borrow_mut() += 1;
 9677                    }
 9678                },
 9679            )
 9680            .detach();
 9681        }
 9682    });
 9683
 9684    // Update the selections only
 9685    _ = leader.update(cx, |leader, cx| {
 9686        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9687    });
 9688    follower
 9689        .update(cx, |follower, cx| {
 9690            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9691        })
 9692        .unwrap()
 9693        .await
 9694        .unwrap();
 9695    _ = follower.update(cx, |follower, cx| {
 9696        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9697    });
 9698    assert!(*is_still_following.borrow());
 9699    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9700
 9701    // Update the scroll position only
 9702    _ = leader.update(cx, |leader, cx| {
 9703        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9704    });
 9705    follower
 9706        .update(cx, |follower, cx| {
 9707            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9708        })
 9709        .unwrap()
 9710        .await
 9711        .unwrap();
 9712    assert_eq!(
 9713        follower
 9714            .update(cx, |follower, cx| follower.scroll_position(cx))
 9715            .unwrap(),
 9716        gpui::Point::new(1.5, 3.5)
 9717    );
 9718    assert!(*is_still_following.borrow());
 9719    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9720
 9721    // Update the selections and scroll position. The follower's scroll position is updated
 9722    // via autoscroll, not via the leader's exact scroll position.
 9723    _ = leader.update(cx, |leader, cx| {
 9724        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9725        leader.request_autoscroll(Autoscroll::newest(), cx);
 9726        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9727    });
 9728    follower
 9729        .update(cx, |follower, cx| {
 9730            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9731        })
 9732        .unwrap()
 9733        .await
 9734        .unwrap();
 9735    _ = follower.update(cx, |follower, cx| {
 9736        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9737        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9738    });
 9739    assert!(*is_still_following.borrow());
 9740
 9741    // Creating a pending selection that precedes another selection
 9742    _ = leader.update(cx, |leader, cx| {
 9743        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9744        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9745    });
 9746    follower
 9747        .update(cx, |follower, cx| {
 9748            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9749        })
 9750        .unwrap()
 9751        .await
 9752        .unwrap();
 9753    _ = follower.update(cx, |follower, cx| {
 9754        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9755    });
 9756    assert!(*is_still_following.borrow());
 9757
 9758    // Extend the pending selection so that it surrounds another selection
 9759    _ = leader.update(cx, |leader, cx| {
 9760        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9761    });
 9762    follower
 9763        .update(cx, |follower, cx| {
 9764            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9765        })
 9766        .unwrap()
 9767        .await
 9768        .unwrap();
 9769    _ = follower.update(cx, |follower, cx| {
 9770        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9771    });
 9772
 9773    // Scrolling locally breaks the follow
 9774    _ = follower.update(cx, |follower, cx| {
 9775        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9776        follower.set_scroll_anchor(
 9777            ScrollAnchor {
 9778                anchor: top_anchor,
 9779                offset: gpui::Point::new(0.0, 0.5),
 9780            },
 9781            cx,
 9782        );
 9783    });
 9784    assert!(!(*is_still_following.borrow()));
 9785}
 9786
 9787#[gpui::test]
 9788async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9789    init_test(cx, |_| {});
 9790
 9791    let fs = FakeFs::new(cx.executor());
 9792    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9793    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9794    let pane = workspace
 9795        .update(cx, |workspace, _| workspace.active_pane().clone())
 9796        .unwrap();
 9797
 9798    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9799
 9800    let leader = pane.update(cx, |_, cx| {
 9801        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9802        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9803    });
 9804
 9805    // Start following the editor when it has no excerpts.
 9806    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9807    let follower_1 = cx
 9808        .update_window(*workspace.deref(), |_, cx| {
 9809            Editor::from_state_proto(
 9810                workspace.root_view(cx).unwrap(),
 9811                ViewId {
 9812                    creator: Default::default(),
 9813                    id: 0,
 9814                },
 9815                &mut state_message,
 9816                cx,
 9817            )
 9818        })
 9819        .unwrap()
 9820        .unwrap()
 9821        .await
 9822        .unwrap();
 9823
 9824    let update_message = Rc::new(RefCell::new(None));
 9825    follower_1.update(cx, {
 9826        let update = update_message.clone();
 9827        |_, cx| {
 9828            cx.subscribe(&leader, move |_, leader, event, cx| {
 9829                leader
 9830                    .read(cx)
 9831                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9832            })
 9833            .detach();
 9834        }
 9835    });
 9836
 9837    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9838        (
 9839            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9840            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9841        )
 9842    });
 9843
 9844    // Insert some excerpts.
 9845    leader.update(cx, |leader, cx| {
 9846        leader.buffer.update(cx, |multibuffer, cx| {
 9847            let excerpt_ids = multibuffer.push_excerpts(
 9848                buffer_1.clone(),
 9849                [
 9850                    ExcerptRange {
 9851                        context: 1..6,
 9852                        primary: None,
 9853                    },
 9854                    ExcerptRange {
 9855                        context: 12..15,
 9856                        primary: None,
 9857                    },
 9858                    ExcerptRange {
 9859                        context: 0..3,
 9860                        primary: None,
 9861                    },
 9862                ],
 9863                cx,
 9864            );
 9865            multibuffer.insert_excerpts_after(
 9866                excerpt_ids[0],
 9867                buffer_2.clone(),
 9868                [
 9869                    ExcerptRange {
 9870                        context: 8..12,
 9871                        primary: None,
 9872                    },
 9873                    ExcerptRange {
 9874                        context: 0..6,
 9875                        primary: None,
 9876                    },
 9877                ],
 9878                cx,
 9879            );
 9880        });
 9881    });
 9882
 9883    // Apply the update of adding the excerpts.
 9884    follower_1
 9885        .update(cx, |follower, cx| {
 9886            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9887        })
 9888        .await
 9889        .unwrap();
 9890    assert_eq!(
 9891        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9892        leader.update(cx, |editor, cx| editor.text(cx))
 9893    );
 9894    update_message.borrow_mut().take();
 9895
 9896    // Start following separately after it already has excerpts.
 9897    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9898    let follower_2 = cx
 9899        .update_window(*workspace.deref(), |_, cx| {
 9900            Editor::from_state_proto(
 9901                workspace.root_view(cx).unwrap().clone(),
 9902                ViewId {
 9903                    creator: Default::default(),
 9904                    id: 0,
 9905                },
 9906                &mut state_message,
 9907                cx,
 9908            )
 9909        })
 9910        .unwrap()
 9911        .unwrap()
 9912        .await
 9913        .unwrap();
 9914    assert_eq!(
 9915        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9916        leader.update(cx, |editor, cx| editor.text(cx))
 9917    );
 9918
 9919    // Remove some excerpts.
 9920    leader.update(cx, |leader, cx| {
 9921        leader.buffer.update(cx, |multibuffer, cx| {
 9922            let excerpt_ids = multibuffer.excerpt_ids();
 9923            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9924            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9925        });
 9926    });
 9927
 9928    // Apply the update of removing the excerpts.
 9929    follower_1
 9930        .update(cx, |follower, cx| {
 9931            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9932        })
 9933        .await
 9934        .unwrap();
 9935    follower_2
 9936        .update(cx, |follower, cx| {
 9937            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9938        })
 9939        .await
 9940        .unwrap();
 9941    update_message.borrow_mut().take();
 9942    assert_eq!(
 9943        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9944        leader.update(cx, |editor, cx| editor.text(cx))
 9945    );
 9946}
 9947
 9948#[gpui::test]
 9949async fn go_to_prev_overlapping_diagnostic(
 9950    executor: BackgroundExecutor,
 9951    cx: &mut gpui::TestAppContext,
 9952) {
 9953    init_test(cx, |_| {});
 9954
 9955    let mut cx = EditorTestContext::new(cx).await;
 9956    let lsp_store =
 9957        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
 9958
 9959    cx.set_state(indoc! {"
 9960        ˇfn func(abc def: i32) -> u32 {
 9961        }
 9962    "});
 9963
 9964    cx.update(|cx| {
 9965        lsp_store.update(cx, |lsp_store, cx| {
 9966            lsp_store
 9967                .update_diagnostics(
 9968                    LanguageServerId(0),
 9969                    lsp::PublishDiagnosticsParams {
 9970                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9971                        version: None,
 9972                        diagnostics: vec![
 9973                            lsp::Diagnostic {
 9974                                range: lsp::Range::new(
 9975                                    lsp::Position::new(0, 11),
 9976                                    lsp::Position::new(0, 12),
 9977                                ),
 9978                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9979                                ..Default::default()
 9980                            },
 9981                            lsp::Diagnostic {
 9982                                range: lsp::Range::new(
 9983                                    lsp::Position::new(0, 12),
 9984                                    lsp::Position::new(0, 15),
 9985                                ),
 9986                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9987                                ..Default::default()
 9988                            },
 9989                            lsp::Diagnostic {
 9990                                range: lsp::Range::new(
 9991                                    lsp::Position::new(0, 25),
 9992                                    lsp::Position::new(0, 28),
 9993                                ),
 9994                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9995                                ..Default::default()
 9996                            },
 9997                        ],
 9998                    },
 9999                    &[],
10000                    cx,
10001                )
10002                .unwrap()
10003        });
10004    });
10005
10006    executor.run_until_parked();
10007
10008    cx.update_editor(|editor, cx| {
10009        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10010    });
10011
10012    cx.assert_editor_state(indoc! {"
10013        fn func(abc def: i32) -> ˇu32 {
10014        }
10015    "});
10016
10017    cx.update_editor(|editor, cx| {
10018        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10019    });
10020
10021    cx.assert_editor_state(indoc! {"
10022        fn func(abc ˇdef: i32) -> u32 {
10023        }
10024    "});
10025
10026    cx.update_editor(|editor, cx| {
10027        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10028    });
10029
10030    cx.assert_editor_state(indoc! {"
10031        fn func(abcˇ def: i32) -> u32 {
10032        }
10033    "});
10034
10035    cx.update_editor(|editor, cx| {
10036        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10037    });
10038
10039    cx.assert_editor_state(indoc! {"
10040        fn func(abc def: i32) -> ˇu32 {
10041        }
10042    "});
10043}
10044
10045#[gpui::test]
10046async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10047    init_test(cx, |_| {});
10048
10049    let mut cx = EditorTestContext::new(cx).await;
10050
10051    cx.set_state(indoc! {"
10052        fn func(abˇc def: i32) -> u32 {
10053        }
10054    "});
10055    let lsp_store =
10056        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10057
10058    cx.update(|cx| {
10059        lsp_store.update(cx, |lsp_store, cx| {
10060            lsp_store.update_diagnostics(
10061                LanguageServerId(0),
10062                lsp::PublishDiagnosticsParams {
10063                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10064                    version: None,
10065                    diagnostics: vec![lsp::Diagnostic {
10066                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10067                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10068                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10069                        ..Default::default()
10070                    }],
10071                },
10072                &[],
10073                cx,
10074            )
10075        })
10076    }).unwrap();
10077    cx.run_until_parked();
10078    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10079    cx.run_until_parked();
10080    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10081}
10082
10083#[gpui::test]
10084async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10085    init_test(cx, |_| {});
10086
10087    let mut cx = EditorTestContext::new(cx).await;
10088
10089    let diff_base = r#"
10090        use some::mod;
10091
10092        const A: u32 = 42;
10093
10094        fn main() {
10095            println!("hello");
10096
10097            println!("world");
10098        }
10099        "#
10100    .unindent();
10101
10102    // Edits are modified, removed, modified, added
10103    cx.set_state(
10104        &r#"
10105        use some::modified;
10106
10107        ˇ
10108        fn main() {
10109            println!("hello there");
10110
10111            println!("around the");
10112            println!("world");
10113        }
10114        "#
10115        .unindent(),
10116    );
10117
10118    cx.set_diff_base(&diff_base);
10119    executor.run_until_parked();
10120
10121    cx.update_editor(|editor, cx| {
10122        //Wrap around the bottom of the buffer
10123        for _ in 0..3 {
10124            editor.go_to_next_hunk(&GoToHunk, cx);
10125        }
10126    });
10127
10128    cx.assert_editor_state(
10129        &r#"
10130        ˇuse some::modified;
10131
10132
10133        fn main() {
10134            println!("hello there");
10135
10136            println!("around the");
10137            println!("world");
10138        }
10139        "#
10140        .unindent(),
10141    );
10142
10143    cx.update_editor(|editor, cx| {
10144        //Wrap around the top of the buffer
10145        for _ in 0..2 {
10146            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10147        }
10148    });
10149
10150    cx.assert_editor_state(
10151        &r#"
10152        use some::modified;
10153
10154
10155        fn main() {
10156        ˇ    println!("hello there");
10157
10158            println!("around the");
10159            println!("world");
10160        }
10161        "#
10162        .unindent(),
10163    );
10164
10165    cx.update_editor(|editor, cx| {
10166        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10167    });
10168
10169    cx.assert_editor_state(
10170        &r#"
10171        use some::modified;
10172
10173        ˇ
10174        fn main() {
10175            println!("hello there");
10176
10177            println!("around the");
10178            println!("world");
10179        }
10180        "#
10181        .unindent(),
10182    );
10183
10184    cx.update_editor(|editor, cx| {
10185        for _ in 0..3 {
10186            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10187        }
10188    });
10189
10190    cx.assert_editor_state(
10191        &r#"
10192        use some::modified;
10193
10194
10195        fn main() {
10196        ˇ    println!("hello there");
10197
10198            println!("around the");
10199            println!("world");
10200        }
10201        "#
10202        .unindent(),
10203    );
10204
10205    cx.update_editor(|editor, cx| {
10206        editor.fold(&Fold, cx);
10207
10208        //Make sure that the fold only gets one hunk
10209        for _ in 0..4 {
10210            editor.go_to_next_hunk(&GoToHunk, cx);
10211        }
10212    });
10213
10214    cx.assert_editor_state(
10215        &r#"
10216        ˇuse some::modified;
10217
10218
10219        fn main() {
10220            println!("hello there");
10221
10222            println!("around the");
10223            println!("world");
10224        }
10225        "#
10226        .unindent(),
10227    );
10228}
10229
10230#[test]
10231fn test_split_words() {
10232    fn split(text: &str) -> Vec<&str> {
10233        split_words(text).collect()
10234    }
10235
10236    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10237    assert_eq!(split("hello_world"), &["hello_", "world"]);
10238    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10239    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10240    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10241    assert_eq!(split("helloworld"), &["helloworld"]);
10242
10243    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10244}
10245
10246#[gpui::test]
10247async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10248    init_test(cx, |_| {});
10249
10250    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10251    let mut assert = |before, after| {
10252        let _state_context = cx.set_state(before);
10253        cx.update_editor(|editor, cx| {
10254            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10255        });
10256        cx.assert_editor_state(after);
10257    };
10258
10259    // Outside bracket jumps to outside of matching bracket
10260    assert("console.logˇ(var);", "console.log(var)ˇ;");
10261    assert("console.log(var)ˇ;", "console.logˇ(var);");
10262
10263    // Inside bracket jumps to inside of matching bracket
10264    assert("console.log(ˇvar);", "console.log(varˇ);");
10265    assert("console.log(varˇ);", "console.log(ˇvar);");
10266
10267    // When outside a bracket and inside, favor jumping to the inside bracket
10268    assert(
10269        "console.log('foo', [1, 2, 3]ˇ);",
10270        "console.log(ˇ'foo', [1, 2, 3]);",
10271    );
10272    assert(
10273        "console.log(ˇ'foo', [1, 2, 3]);",
10274        "console.log('foo', [1, 2, 3]ˇ);",
10275    );
10276
10277    // Bias forward if two options are equally likely
10278    assert(
10279        "let result = curried_fun()ˇ();",
10280        "let result = curried_fun()()ˇ;",
10281    );
10282
10283    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10284    assert(
10285        indoc! {"
10286            function test() {
10287                console.log('test')ˇ
10288            }"},
10289        indoc! {"
10290            function test() {
10291                console.logˇ('test')
10292            }"},
10293    );
10294}
10295
10296#[gpui::test]
10297async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10298    init_test(cx, |_| {});
10299
10300    let fs = FakeFs::new(cx.executor());
10301    fs.insert_tree(
10302        "/a",
10303        json!({
10304            "main.rs": "fn main() { let a = 5; }",
10305            "other.rs": "// Test file",
10306        }),
10307    )
10308    .await;
10309    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10310
10311    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10312    language_registry.add(Arc::new(Language::new(
10313        LanguageConfig {
10314            name: "Rust".into(),
10315            matcher: LanguageMatcher {
10316                path_suffixes: vec!["rs".to_string()],
10317                ..Default::default()
10318            },
10319            brackets: BracketPairConfig {
10320                pairs: vec![BracketPair {
10321                    start: "{".to_string(),
10322                    end: "}".to_string(),
10323                    close: true,
10324                    surround: true,
10325                    newline: true,
10326                }],
10327                disabled_scopes_by_bracket_ix: Vec::new(),
10328            },
10329            ..Default::default()
10330        },
10331        Some(tree_sitter_rust::LANGUAGE.into()),
10332    )));
10333    let mut fake_servers = language_registry.register_fake_lsp(
10334        "Rust",
10335        FakeLspAdapter {
10336            capabilities: lsp::ServerCapabilities {
10337                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10338                    first_trigger_character: "{".to_string(),
10339                    more_trigger_character: None,
10340                }),
10341                ..Default::default()
10342            },
10343            ..Default::default()
10344        },
10345    );
10346
10347    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10348
10349    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10350
10351    let worktree_id = workspace
10352        .update(cx, |workspace, cx| {
10353            workspace.project().update(cx, |project, cx| {
10354                project.worktrees(cx).next().unwrap().read(cx).id()
10355            })
10356        })
10357        .unwrap();
10358
10359    let buffer = project
10360        .update(cx, |project, cx| {
10361            project.open_local_buffer("/a/main.rs", cx)
10362        })
10363        .await
10364        .unwrap();
10365    let editor_handle = workspace
10366        .update(cx, |workspace, cx| {
10367            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10368        })
10369        .unwrap()
10370        .await
10371        .unwrap()
10372        .downcast::<Editor>()
10373        .unwrap();
10374
10375    cx.executor().start_waiting();
10376    let fake_server = fake_servers.next().await.unwrap();
10377
10378    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10379        assert_eq!(
10380            params.text_document_position.text_document.uri,
10381            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10382        );
10383        assert_eq!(
10384            params.text_document_position.position,
10385            lsp::Position::new(0, 21),
10386        );
10387
10388        Ok(Some(vec![lsp::TextEdit {
10389            new_text: "]".to_string(),
10390            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10391        }]))
10392    });
10393
10394    editor_handle.update(cx, |editor, cx| {
10395        editor.focus(cx);
10396        editor.change_selections(None, cx, |s| {
10397            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10398        });
10399        editor.handle_input("{", cx);
10400    });
10401
10402    cx.executor().run_until_parked();
10403
10404    buffer.update(cx, |buffer, _| {
10405        assert_eq!(
10406            buffer.text(),
10407            "fn main() { let a = {5}; }",
10408            "No extra braces from on type formatting should appear in the buffer"
10409        )
10410    });
10411}
10412
10413#[gpui::test]
10414async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10415    init_test(cx, |_| {});
10416
10417    let fs = FakeFs::new(cx.executor());
10418    fs.insert_tree(
10419        "/a",
10420        json!({
10421            "main.rs": "fn main() { let a = 5; }",
10422            "other.rs": "// Test file",
10423        }),
10424    )
10425    .await;
10426
10427    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10428
10429    let server_restarts = Arc::new(AtomicUsize::new(0));
10430    let closure_restarts = Arc::clone(&server_restarts);
10431    let language_server_name = "test language server";
10432    let language_name: LanguageName = "Rust".into();
10433
10434    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10435    language_registry.add(Arc::new(Language::new(
10436        LanguageConfig {
10437            name: language_name.clone(),
10438            matcher: LanguageMatcher {
10439                path_suffixes: vec!["rs".to_string()],
10440                ..Default::default()
10441            },
10442            ..Default::default()
10443        },
10444        Some(tree_sitter_rust::LANGUAGE.into()),
10445    )));
10446    let mut fake_servers = language_registry.register_fake_lsp(
10447        "Rust",
10448        FakeLspAdapter {
10449            name: language_server_name,
10450            initialization_options: Some(json!({
10451                "testOptionValue": true
10452            })),
10453            initializer: Some(Box::new(move |fake_server| {
10454                let task_restarts = Arc::clone(&closure_restarts);
10455                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10456                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10457                    futures::future::ready(Ok(()))
10458                });
10459            })),
10460            ..Default::default()
10461        },
10462    );
10463
10464    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10465    let _buffer = project
10466        .update(cx, |project, cx| {
10467            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10468        })
10469        .await
10470        .unwrap();
10471    let _fake_server = fake_servers.next().await.unwrap();
10472    update_test_language_settings(cx, |language_settings| {
10473        language_settings.languages.insert(
10474            language_name.clone(),
10475            LanguageSettingsContent {
10476                tab_size: NonZeroU32::new(8),
10477                ..Default::default()
10478            },
10479        );
10480    });
10481    cx.executor().run_until_parked();
10482    assert_eq!(
10483        server_restarts.load(atomic::Ordering::Acquire),
10484        0,
10485        "Should not restart LSP server on an unrelated change"
10486    );
10487
10488    update_test_project_settings(cx, |project_settings| {
10489        project_settings.lsp.insert(
10490            "Some other server name".into(),
10491            LspSettings {
10492                binary: None,
10493                settings: None,
10494                initialization_options: Some(json!({
10495                    "some other init value": false
10496                })),
10497            },
10498        );
10499    });
10500    cx.executor().run_until_parked();
10501    assert_eq!(
10502        server_restarts.load(atomic::Ordering::Acquire),
10503        0,
10504        "Should not restart LSP server on an unrelated LSP settings change"
10505    );
10506
10507    update_test_project_settings(cx, |project_settings| {
10508        project_settings.lsp.insert(
10509            language_server_name.into(),
10510            LspSettings {
10511                binary: None,
10512                settings: None,
10513                initialization_options: Some(json!({
10514                    "anotherInitValue": false
10515                })),
10516            },
10517        );
10518    });
10519    cx.executor().run_until_parked();
10520    assert_eq!(
10521        server_restarts.load(atomic::Ordering::Acquire),
10522        1,
10523        "Should restart LSP server on a related LSP settings change"
10524    );
10525
10526    update_test_project_settings(cx, |project_settings| {
10527        project_settings.lsp.insert(
10528            language_server_name.into(),
10529            LspSettings {
10530                binary: None,
10531                settings: None,
10532                initialization_options: Some(json!({
10533                    "anotherInitValue": false
10534                })),
10535            },
10536        );
10537    });
10538    cx.executor().run_until_parked();
10539    assert_eq!(
10540        server_restarts.load(atomic::Ordering::Acquire),
10541        1,
10542        "Should not restart LSP server on a related LSP settings change that is the same"
10543    );
10544
10545    update_test_project_settings(cx, |project_settings| {
10546        project_settings.lsp.insert(
10547            language_server_name.into(),
10548            LspSettings {
10549                binary: None,
10550                settings: None,
10551                initialization_options: None,
10552            },
10553        );
10554    });
10555    cx.executor().run_until_parked();
10556    assert_eq!(
10557        server_restarts.load(atomic::Ordering::Acquire),
10558        2,
10559        "Should restart LSP server on another related LSP settings change"
10560    );
10561}
10562
10563#[gpui::test]
10564async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10565    init_test(cx, |_| {});
10566
10567    let mut cx = EditorLspTestContext::new_rust(
10568        lsp::ServerCapabilities {
10569            completion_provider: Some(lsp::CompletionOptions {
10570                trigger_characters: Some(vec![".".to_string()]),
10571                resolve_provider: Some(true),
10572                ..Default::default()
10573            }),
10574            ..Default::default()
10575        },
10576        cx,
10577    )
10578    .await;
10579
10580    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10581    cx.simulate_keystroke(".");
10582    let completion_item = lsp::CompletionItem {
10583        label: "some".into(),
10584        kind: Some(lsp::CompletionItemKind::SNIPPET),
10585        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10586        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10587            kind: lsp::MarkupKind::Markdown,
10588            value: "```rust\nSome(2)\n```".to_string(),
10589        })),
10590        deprecated: Some(false),
10591        sort_text: Some("fffffff2".to_string()),
10592        filter_text: Some("some".to_string()),
10593        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10594        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10595            range: lsp::Range {
10596                start: lsp::Position {
10597                    line: 0,
10598                    character: 22,
10599                },
10600                end: lsp::Position {
10601                    line: 0,
10602                    character: 22,
10603                },
10604            },
10605            new_text: "Some(2)".to_string(),
10606        })),
10607        additional_text_edits: Some(vec![lsp::TextEdit {
10608            range: lsp::Range {
10609                start: lsp::Position {
10610                    line: 0,
10611                    character: 20,
10612                },
10613                end: lsp::Position {
10614                    line: 0,
10615                    character: 22,
10616                },
10617            },
10618            new_text: "".to_string(),
10619        }]),
10620        ..Default::default()
10621    };
10622
10623    let closure_completion_item = completion_item.clone();
10624    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10625        let task_completion_item = closure_completion_item.clone();
10626        async move {
10627            Ok(Some(lsp::CompletionResponse::Array(vec![
10628                task_completion_item,
10629            ])))
10630        }
10631    });
10632
10633    request.next().await;
10634
10635    cx.condition(|editor, _| editor.context_menu_visible())
10636        .await;
10637    let apply_additional_edits = cx.update_editor(|editor, cx| {
10638        editor
10639            .confirm_completion(&ConfirmCompletion::default(), cx)
10640            .unwrap()
10641    });
10642    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10643
10644    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10645        let task_completion_item = completion_item.clone();
10646        async move { Ok(task_completion_item) }
10647    })
10648    .next()
10649    .await
10650    .unwrap();
10651    apply_additional_edits.await.unwrap();
10652    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10653}
10654
10655#[gpui::test]
10656async fn test_completions_resolve_updates_labels_if_filter_text_matches(
10657    cx: &mut gpui::TestAppContext,
10658) {
10659    init_test(cx, |_| {});
10660
10661    let mut cx = EditorLspTestContext::new_rust(
10662        lsp::ServerCapabilities {
10663            completion_provider: Some(lsp::CompletionOptions {
10664                trigger_characters: Some(vec![".".to_string()]),
10665                resolve_provider: Some(true),
10666                ..Default::default()
10667            }),
10668            ..Default::default()
10669        },
10670        cx,
10671    )
10672    .await;
10673
10674    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10675    cx.simulate_keystroke(".");
10676
10677    let item1 = lsp::CompletionItem {
10678        label: "id".to_string(),
10679        filter_text: Some("id".to_string()),
10680        detail: None,
10681        documentation: None,
10682        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10683            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10684            new_text: ".id".to_string(),
10685        })),
10686        ..lsp::CompletionItem::default()
10687    };
10688
10689    let item2 = lsp::CompletionItem {
10690        label: "other".to_string(),
10691        filter_text: Some("other".to_string()),
10692        detail: None,
10693        documentation: None,
10694        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10695            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10696            new_text: ".other".to_string(),
10697        })),
10698        ..lsp::CompletionItem::default()
10699    };
10700
10701    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10702        let item1 = item1.clone();
10703        let item2 = item2.clone();
10704        async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
10705    })
10706    .next()
10707    .await;
10708
10709    cx.condition(|editor, _| editor.context_menu_visible())
10710        .await;
10711    cx.update_editor(|editor, _| {
10712        let context_menu = editor.context_menu.borrow_mut();
10713        let context_menu = context_menu
10714            .as_ref()
10715            .expect("Should have the context menu deployed");
10716        match context_menu {
10717            CodeContextMenu::Completions(completions_menu) => {
10718                let completions = completions_menu.completions.borrow_mut();
10719                assert_eq!(
10720                    completions
10721                        .iter()
10722                        .map(|completion| &completion.label.text)
10723                        .collect::<Vec<_>>(),
10724                    vec!["id", "other"]
10725                )
10726            }
10727            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10728        }
10729    });
10730
10731    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10732        Ok(lsp::CompletionItem {
10733            label: "method id()".to_string(),
10734            filter_text: Some("id".to_string()),
10735            detail: Some("Now resolved!".to_string()),
10736            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10737            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10738                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10739                new_text: ".id".to_string(),
10740            })),
10741            ..lsp::CompletionItem::default()
10742        })
10743    })
10744    .next()
10745    .await;
10746    cx.run_until_parked();
10747
10748    cx.update_editor(|editor, cx| {
10749        editor.context_menu_next(&Default::default(), cx);
10750    });
10751
10752    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10753        Ok(lsp::CompletionItem {
10754            label: "invalid changed label".to_string(),
10755            detail: Some("Now resolved!".to_string()),
10756            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10757            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10758                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10759                new_text: ".id".to_string(),
10760            })),
10761            ..lsp::CompletionItem::default()
10762        })
10763    })
10764    .next()
10765    .await;
10766    cx.run_until_parked();
10767
10768    cx.update_editor(|editor, _| {
10769        let context_menu = editor.context_menu.borrow_mut();
10770        let context_menu = context_menu
10771            .as_ref()
10772            .expect("Should have the context menu deployed");
10773        match context_menu {
10774            CodeContextMenu::Completions(completions_menu) => {
10775                let completions = completions_menu.completions.borrow_mut();
10776                assert_eq!(
10777                    completions
10778                        .iter()
10779                        .map(|completion| &completion.label.text)
10780                        .collect::<Vec<_>>(),
10781                    vec!["method id()", "other"],
10782                    "Should update first completion label, but not second as the filter text did not match."
10783                );
10784            }
10785            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10786        }
10787    });
10788}
10789
10790#[gpui::test]
10791async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10792    init_test(cx, |_| {});
10793
10794    let item_0 = lsp::CompletionItem {
10795        label: "abs".into(),
10796        insert_text: Some("abs".into()),
10797        data: Some(json!({ "very": "special"})),
10798        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10799        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10800            lsp::InsertReplaceEdit {
10801                new_text: "abs".to_string(),
10802                insert: lsp::Range::default(),
10803                replace: lsp::Range::default(),
10804            },
10805        )),
10806        ..lsp::CompletionItem::default()
10807    };
10808    let items = iter::once(item_0.clone())
10809        .chain((11..51).map(|i| lsp::CompletionItem {
10810            label: format!("item_{}", i),
10811            insert_text: Some(format!("item_{}", i)),
10812            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10813            ..lsp::CompletionItem::default()
10814        }))
10815        .collect::<Vec<_>>();
10816
10817    let default_commit_characters = vec!["?".to_string()];
10818    let default_data = json!({ "default": "data"});
10819    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10820    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10821    let default_edit_range = lsp::Range {
10822        start: lsp::Position {
10823            line: 0,
10824            character: 5,
10825        },
10826        end: lsp::Position {
10827            line: 0,
10828            character: 5,
10829        },
10830    };
10831
10832    let item_0_out = lsp::CompletionItem {
10833        commit_characters: Some(default_commit_characters.clone()),
10834        insert_text_format: Some(default_insert_text_format),
10835        ..item_0
10836    };
10837    let items_out = iter::once(item_0_out)
10838        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
10839            commit_characters: Some(default_commit_characters.clone()),
10840            data: Some(default_data.clone()),
10841            insert_text_mode: Some(default_insert_text_mode),
10842            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10843                range: default_edit_range,
10844                new_text: item.label.clone(),
10845            })),
10846            ..item.clone()
10847        }))
10848        .collect::<Vec<lsp::CompletionItem>>();
10849
10850    let mut cx = EditorLspTestContext::new_rust(
10851        lsp::ServerCapabilities {
10852            completion_provider: Some(lsp::CompletionOptions {
10853                trigger_characters: Some(vec![".".to_string()]),
10854                resolve_provider: Some(true),
10855                ..Default::default()
10856            }),
10857            ..Default::default()
10858        },
10859        cx,
10860    )
10861    .await;
10862
10863    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10864    cx.simulate_keystroke(".");
10865
10866    let completion_data = default_data.clone();
10867    let completion_characters = default_commit_characters.clone();
10868    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10869        let default_data = completion_data.clone();
10870        let default_commit_characters = completion_characters.clone();
10871        let items = items.clone();
10872        async move {
10873            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10874                items,
10875                item_defaults: Some(lsp::CompletionListItemDefaults {
10876                    data: Some(default_data.clone()),
10877                    commit_characters: Some(default_commit_characters.clone()),
10878                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10879                        default_edit_range,
10880                    )),
10881                    insert_text_format: Some(default_insert_text_format),
10882                    insert_text_mode: Some(default_insert_text_mode),
10883                }),
10884                ..lsp::CompletionList::default()
10885            })))
10886        }
10887    })
10888    .next()
10889    .await;
10890
10891    let resolved_items = Arc::new(Mutex::new(Vec::new()));
10892    cx.lsp
10893        .server
10894        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10895            let closure_resolved_items = resolved_items.clone();
10896            move |item_to_resolve, _| {
10897                let closure_resolved_items = closure_resolved_items.clone();
10898                async move {
10899                    closure_resolved_items.lock().push(item_to_resolve.clone());
10900                    Ok(item_to_resolve)
10901                }
10902            }
10903        })
10904        .detach();
10905
10906    cx.condition(|editor, _| editor.context_menu_visible())
10907        .await;
10908    cx.run_until_parked();
10909    cx.update_editor(|editor, _| {
10910        let menu = editor.context_menu.borrow_mut();
10911        match menu.as_ref().expect("should have the completions menu") {
10912            CodeContextMenu::Completions(completions_menu) => {
10913                assert_eq!(
10914                    completions_menu
10915                        .entries
10916                        .iter()
10917                        .flat_map(|c| match c {
10918                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
10919                            _ => None,
10920                        })
10921                        .collect::<Vec<String>>(),
10922                    items_out
10923                        .iter()
10924                        .map(|completion| completion.label.clone())
10925                        .collect::<Vec<String>>()
10926                );
10927            }
10928            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10929        }
10930    });
10931    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
10932    // with 4 from the end.
10933    assert_eq!(
10934        *resolved_items.lock(),
10935        [
10936            &items_out[0..16],
10937            &items_out[items_out.len() - 4..items_out.len()]
10938        ]
10939        .concat()
10940        .iter()
10941        .cloned()
10942        .collect::<Vec<lsp::CompletionItem>>()
10943    );
10944    resolved_items.lock().clear();
10945
10946    cx.update_editor(|editor, cx| {
10947        editor.context_menu_prev(&ContextMenuPrev, cx);
10948    });
10949    cx.run_until_parked();
10950    // Completions that have already been resolved are skipped.
10951    assert_eq!(
10952        *resolved_items.lock(),
10953        [
10954            // Selected item is always resolved even if it was resolved before.
10955            &items_out[items_out.len() - 1..items_out.len()],
10956            &items_out[items_out.len() - 16..items_out.len() - 4]
10957        ]
10958        .concat()
10959        .iter()
10960        .cloned()
10961        .collect::<Vec<lsp::CompletionItem>>()
10962    );
10963    resolved_items.lock().clear();
10964}
10965
10966#[gpui::test]
10967async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10968    init_test(cx, |_| {});
10969
10970    let mut cx = EditorLspTestContext::new(
10971        Language::new(
10972            LanguageConfig {
10973                matcher: LanguageMatcher {
10974                    path_suffixes: vec!["jsx".into()],
10975                    ..Default::default()
10976                },
10977                overrides: [(
10978                    "element".into(),
10979                    LanguageConfigOverride {
10980                        word_characters: Override::Set(['-'].into_iter().collect()),
10981                        ..Default::default()
10982                    },
10983                )]
10984                .into_iter()
10985                .collect(),
10986                ..Default::default()
10987            },
10988            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10989        )
10990        .with_override_query("(jsx_self_closing_element) @element")
10991        .unwrap(),
10992        lsp::ServerCapabilities {
10993            completion_provider: Some(lsp::CompletionOptions {
10994                trigger_characters: Some(vec![":".to_string()]),
10995                ..Default::default()
10996            }),
10997            ..Default::default()
10998        },
10999        cx,
11000    )
11001    .await;
11002
11003    cx.lsp
11004        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11005            Ok(Some(lsp::CompletionResponse::Array(vec![
11006                lsp::CompletionItem {
11007                    label: "bg-blue".into(),
11008                    ..Default::default()
11009                },
11010                lsp::CompletionItem {
11011                    label: "bg-red".into(),
11012                    ..Default::default()
11013                },
11014                lsp::CompletionItem {
11015                    label: "bg-yellow".into(),
11016                    ..Default::default()
11017                },
11018            ])))
11019        });
11020
11021    cx.set_state(r#"<p class="bgˇ" />"#);
11022
11023    // Trigger completion when typing a dash, because the dash is an extra
11024    // word character in the 'element' scope, which contains the cursor.
11025    cx.simulate_keystroke("-");
11026    cx.executor().run_until_parked();
11027    cx.update_editor(|editor, _| {
11028        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11029        {
11030            assert_eq!(
11031                completion_menu_entries(&menu.entries),
11032                &["bg-red", "bg-blue", "bg-yellow"]
11033            );
11034        } else {
11035            panic!("expected completion menu to be open");
11036        }
11037    });
11038
11039    cx.simulate_keystroke("l");
11040    cx.executor().run_until_parked();
11041    cx.update_editor(|editor, _| {
11042        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11043        {
11044            assert_eq!(
11045                completion_menu_entries(&menu.entries),
11046                &["bg-blue", "bg-yellow"]
11047            );
11048        } else {
11049            panic!("expected completion menu to be open");
11050        }
11051    });
11052
11053    // When filtering completions, consider the character after the '-' to
11054    // be the start of a subword.
11055    cx.set_state(r#"<p class="yelˇ" />"#);
11056    cx.simulate_keystroke("l");
11057    cx.executor().run_until_parked();
11058    cx.update_editor(|editor, _| {
11059        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11060        {
11061            assert_eq!(completion_menu_entries(&menu.entries), &["bg-yellow"]);
11062        } else {
11063            panic!("expected completion menu to be open");
11064        }
11065    });
11066}
11067
11068fn completion_menu_entries(entries: &[CompletionEntry]) -> Vec<&str> {
11069    entries
11070        .iter()
11071        .flat_map(|e| match e {
11072            CompletionEntry::Match(mat) => Some(mat.string.as_str()),
11073            _ => None,
11074        })
11075        .collect()
11076}
11077
11078#[gpui::test]
11079async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11080    init_test(cx, |settings| {
11081        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11082            FormatterList(vec![Formatter::Prettier].into()),
11083        ))
11084    });
11085
11086    let fs = FakeFs::new(cx.executor());
11087    fs.insert_file("/file.ts", Default::default()).await;
11088
11089    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11090    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11091
11092    language_registry.add(Arc::new(Language::new(
11093        LanguageConfig {
11094            name: "TypeScript".into(),
11095            matcher: LanguageMatcher {
11096                path_suffixes: vec!["ts".to_string()],
11097                ..Default::default()
11098            },
11099            ..Default::default()
11100        },
11101        Some(tree_sitter_rust::LANGUAGE.into()),
11102    )));
11103    update_test_language_settings(cx, |settings| {
11104        settings.defaults.prettier = Some(PrettierSettings {
11105            allowed: true,
11106            ..PrettierSettings::default()
11107        });
11108    });
11109
11110    let test_plugin = "test_plugin";
11111    let _ = language_registry.register_fake_lsp(
11112        "TypeScript",
11113        FakeLspAdapter {
11114            prettier_plugins: vec![test_plugin],
11115            ..Default::default()
11116        },
11117    );
11118
11119    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11120    let buffer = project
11121        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11122        .await
11123        .unwrap();
11124
11125    let buffer_text = "one\ntwo\nthree\n";
11126    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11127    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11128    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11129
11130    editor
11131        .update(cx, |editor, cx| {
11132            editor.perform_format(
11133                project.clone(),
11134                FormatTrigger::Manual,
11135                FormatTarget::Buffer,
11136                cx,
11137            )
11138        })
11139        .unwrap()
11140        .await;
11141    assert_eq!(
11142        editor.update(cx, |editor, cx| editor.text(cx)),
11143        buffer_text.to_string() + prettier_format_suffix,
11144        "Test prettier formatting was not applied to the original buffer text",
11145    );
11146
11147    update_test_language_settings(cx, |settings| {
11148        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11149    });
11150    let format = editor.update(cx, |editor, cx| {
11151        editor.perform_format(
11152            project.clone(),
11153            FormatTrigger::Manual,
11154            FormatTarget::Buffer,
11155            cx,
11156        )
11157    });
11158    format.await.unwrap();
11159    assert_eq!(
11160        editor.update(cx, |editor, cx| editor.text(cx)),
11161        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11162        "Autoformatting (via test prettier) was not applied to the original buffer text",
11163    );
11164}
11165
11166#[gpui::test]
11167async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11168    init_test(cx, |_| {});
11169    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11170    let base_text = indoc! {r#"
11171        struct Row;
11172        struct Row1;
11173        struct Row2;
11174
11175        struct Row4;
11176        struct Row5;
11177        struct Row6;
11178
11179        struct Row8;
11180        struct Row9;
11181        struct Row10;"#};
11182
11183    // When addition hunks are not adjacent to carets, no hunk revert is performed
11184    assert_hunk_revert(
11185        indoc! {r#"struct Row;
11186                   struct Row1;
11187                   struct Row1.1;
11188                   struct Row1.2;
11189                   struct Row2;ˇ
11190
11191                   struct Row4;
11192                   struct Row5;
11193                   struct Row6;
11194
11195                   struct Row8;
11196                   ˇstruct Row9;
11197                   struct Row9.1;
11198                   struct Row9.2;
11199                   struct Row9.3;
11200                   struct Row10;"#},
11201        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11202        indoc! {r#"struct Row;
11203                   struct Row1;
11204                   struct Row1.1;
11205                   struct Row1.2;
11206                   struct Row2;ˇ
11207
11208                   struct Row4;
11209                   struct Row5;
11210                   struct Row6;
11211
11212                   struct Row8;
11213                   ˇstruct Row9;
11214                   struct Row9.1;
11215                   struct Row9.2;
11216                   struct Row9.3;
11217                   struct Row10;"#},
11218        base_text,
11219        &mut cx,
11220    );
11221    // Same for selections
11222    assert_hunk_revert(
11223        indoc! {r#"struct Row;
11224                   struct Row1;
11225                   struct Row2;
11226                   struct Row2.1;
11227                   struct Row2.2;
11228                   «ˇ
11229                   struct Row4;
11230                   struct» Row5;
11231                   «struct Row6;
11232                   ˇ»
11233                   struct Row9.1;
11234                   struct Row9.2;
11235                   struct Row9.3;
11236                   struct Row8;
11237                   struct Row9;
11238                   struct Row10;"#},
11239        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11240        indoc! {r#"struct Row;
11241                   struct Row1;
11242                   struct Row2;
11243                   struct Row2.1;
11244                   struct Row2.2;
11245                   «ˇ
11246                   struct Row4;
11247                   struct» Row5;
11248                   «struct Row6;
11249                   ˇ»
11250                   struct Row9.1;
11251                   struct Row9.2;
11252                   struct Row9.3;
11253                   struct Row8;
11254                   struct Row9;
11255                   struct Row10;"#},
11256        base_text,
11257        &mut cx,
11258    );
11259
11260    // When carets and selections intersect the addition hunks, those are reverted.
11261    // Adjacent carets got merged.
11262    assert_hunk_revert(
11263        indoc! {r#"struct Row;
11264                   ˇ// something on the top
11265                   struct Row1;
11266                   struct Row2;
11267                   struct Roˇw3.1;
11268                   struct Row2.2;
11269                   struct Row2.3;ˇ
11270
11271                   struct Row4;
11272                   struct ˇRow5.1;
11273                   struct Row5.2;
11274                   struct «Rowˇ»5.3;
11275                   struct Row5;
11276                   struct Row6;
11277                   ˇ
11278                   struct Row9.1;
11279                   struct «Rowˇ»9.2;
11280                   struct «ˇRow»9.3;
11281                   struct Row8;
11282                   struct Row9;
11283                   «ˇ// something on bottom»
11284                   struct Row10;"#},
11285        vec![
11286            DiffHunkStatus::Added,
11287            DiffHunkStatus::Added,
11288            DiffHunkStatus::Added,
11289            DiffHunkStatus::Added,
11290            DiffHunkStatus::Added,
11291        ],
11292        indoc! {r#"struct Row;
11293                   ˇstruct Row1;
11294                   struct Row2;
11295                   ˇ
11296                   struct Row4;
11297                   ˇstruct Row5;
11298                   struct Row6;
11299                   ˇ
11300                   ˇstruct Row8;
11301                   struct Row9;
11302                   ˇstruct Row10;"#},
11303        base_text,
11304        &mut cx,
11305    );
11306}
11307
11308#[gpui::test]
11309async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11310    init_test(cx, |_| {});
11311    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11312    let base_text = indoc! {r#"
11313        struct Row;
11314        struct Row1;
11315        struct Row2;
11316
11317        struct Row4;
11318        struct Row5;
11319        struct Row6;
11320
11321        struct Row8;
11322        struct Row9;
11323        struct Row10;"#};
11324
11325    // Modification hunks behave the same as the addition ones.
11326    assert_hunk_revert(
11327        indoc! {r#"struct Row;
11328                   struct Row1;
11329                   struct Row33;
11330                   ˇ
11331                   struct Row4;
11332                   struct Row5;
11333                   struct Row6;
11334                   ˇ
11335                   struct Row99;
11336                   struct Row9;
11337                   struct Row10;"#},
11338        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11339        indoc! {r#"struct Row;
11340                   struct Row1;
11341                   struct Row33;
11342                   ˇ
11343                   struct Row4;
11344                   struct Row5;
11345                   struct Row6;
11346                   ˇ
11347                   struct Row99;
11348                   struct Row9;
11349                   struct Row10;"#},
11350        base_text,
11351        &mut cx,
11352    );
11353    assert_hunk_revert(
11354        indoc! {r#"struct Row;
11355                   struct Row1;
11356                   struct Row33;
11357                   «ˇ
11358                   struct Row4;
11359                   struct» Row5;
11360                   «struct Row6;
11361                   ˇ»
11362                   struct Row99;
11363                   struct Row9;
11364                   struct Row10;"#},
11365        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11366        indoc! {r#"struct Row;
11367                   struct Row1;
11368                   struct Row33;
11369                   «ˇ
11370                   struct Row4;
11371                   struct» Row5;
11372                   «struct Row6;
11373                   ˇ»
11374                   struct Row99;
11375                   struct Row9;
11376                   struct Row10;"#},
11377        base_text,
11378        &mut cx,
11379    );
11380
11381    assert_hunk_revert(
11382        indoc! {r#"ˇstruct Row1.1;
11383                   struct Row1;
11384                   «ˇstr»uct Row22;
11385
11386                   struct ˇRow44;
11387                   struct Row5;
11388                   struct «Rˇ»ow66;ˇ
11389
11390                   «struˇ»ct Row88;
11391                   struct Row9;
11392                   struct Row1011;ˇ"#},
11393        vec![
11394            DiffHunkStatus::Modified,
11395            DiffHunkStatus::Modified,
11396            DiffHunkStatus::Modified,
11397            DiffHunkStatus::Modified,
11398            DiffHunkStatus::Modified,
11399            DiffHunkStatus::Modified,
11400        ],
11401        indoc! {r#"struct Row;
11402                   ˇstruct Row1;
11403                   struct Row2;
11404                   ˇ
11405                   struct Row4;
11406                   ˇstruct Row5;
11407                   struct Row6;
11408                   ˇ
11409                   struct Row8;
11410                   ˇstruct Row9;
11411                   struct Row10;ˇ"#},
11412        base_text,
11413        &mut cx,
11414    );
11415}
11416
11417#[gpui::test]
11418async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11419    init_test(cx, |_| {});
11420    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11421    let base_text = indoc! {r#"struct Row;
11422struct Row1;
11423struct Row2;
11424
11425struct Row4;
11426struct Row5;
11427struct Row6;
11428
11429struct Row8;
11430struct Row9;
11431struct Row10;"#};
11432
11433    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11434    assert_hunk_revert(
11435        indoc! {r#"struct Row;
11436                   struct Row2;
11437
11438                   ˇstruct Row4;
11439                   struct Row5;
11440                   struct Row6;
11441                   ˇ
11442                   struct Row8;
11443                   struct Row10;"#},
11444        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11445        indoc! {r#"struct Row;
11446                   struct Row2;
11447
11448                   ˇstruct Row4;
11449                   struct Row5;
11450                   struct Row6;
11451                   ˇ
11452                   struct Row8;
11453                   struct Row10;"#},
11454        base_text,
11455        &mut cx,
11456    );
11457    assert_hunk_revert(
11458        indoc! {r#"struct Row;
11459                   struct Row2;
11460
11461                   «ˇstruct Row4;
11462                   struct» Row5;
11463                   «struct Row6;
11464                   ˇ»
11465                   struct Row8;
11466                   struct Row10;"#},
11467        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11468        indoc! {r#"struct Row;
11469                   struct Row2;
11470
11471                   «ˇstruct Row4;
11472                   struct» Row5;
11473                   «struct Row6;
11474                   ˇ»
11475                   struct Row8;
11476                   struct Row10;"#},
11477        base_text,
11478        &mut cx,
11479    );
11480
11481    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11482    assert_hunk_revert(
11483        indoc! {r#"struct Row;
11484                   ˇstruct Row2;
11485
11486                   struct Row4;
11487                   struct Row5;
11488                   struct Row6;
11489
11490                   struct Row8;ˇ
11491                   struct Row10;"#},
11492        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11493        indoc! {r#"struct Row;
11494                   struct Row1;
11495                   ˇstruct Row2;
11496
11497                   struct Row4;
11498                   struct Row5;
11499                   struct Row6;
11500
11501                   struct Row8;ˇ
11502                   struct Row9;
11503                   struct Row10;"#},
11504        base_text,
11505        &mut cx,
11506    );
11507    assert_hunk_revert(
11508        indoc! {r#"struct Row;
11509                   struct Row2«ˇ;
11510                   struct Row4;
11511                   struct» Row5;
11512                   «struct Row6;
11513
11514                   struct Row8;ˇ»
11515                   struct Row10;"#},
11516        vec![
11517            DiffHunkStatus::Removed,
11518            DiffHunkStatus::Removed,
11519            DiffHunkStatus::Removed,
11520        ],
11521        indoc! {r#"struct Row;
11522                   struct Row1;
11523                   struct Row2«ˇ;
11524
11525                   struct Row4;
11526                   struct» Row5;
11527                   «struct Row6;
11528
11529                   struct Row8;ˇ»
11530                   struct Row9;
11531                   struct Row10;"#},
11532        base_text,
11533        &mut cx,
11534    );
11535}
11536
11537#[gpui::test]
11538async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11539    init_test(cx, |_| {});
11540
11541    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11542    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11543    let base_text_3 =
11544        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11545
11546    let text_1 = edit_first_char_of_every_line(base_text_1);
11547    let text_2 = edit_first_char_of_every_line(base_text_2);
11548    let text_3 = edit_first_char_of_every_line(base_text_3);
11549
11550    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11551    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11552    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11553
11554    let multibuffer = cx.new_model(|cx| {
11555        let mut multibuffer = MultiBuffer::new(ReadWrite);
11556        multibuffer.push_excerpts(
11557            buffer_1.clone(),
11558            [
11559                ExcerptRange {
11560                    context: Point::new(0, 0)..Point::new(3, 0),
11561                    primary: None,
11562                },
11563                ExcerptRange {
11564                    context: Point::new(5, 0)..Point::new(7, 0),
11565                    primary: None,
11566                },
11567                ExcerptRange {
11568                    context: Point::new(9, 0)..Point::new(10, 4),
11569                    primary: None,
11570                },
11571            ],
11572            cx,
11573        );
11574        multibuffer.push_excerpts(
11575            buffer_2.clone(),
11576            [
11577                ExcerptRange {
11578                    context: Point::new(0, 0)..Point::new(3, 0),
11579                    primary: None,
11580                },
11581                ExcerptRange {
11582                    context: Point::new(5, 0)..Point::new(7, 0),
11583                    primary: None,
11584                },
11585                ExcerptRange {
11586                    context: Point::new(9, 0)..Point::new(10, 4),
11587                    primary: None,
11588                },
11589            ],
11590            cx,
11591        );
11592        multibuffer.push_excerpts(
11593            buffer_3.clone(),
11594            [
11595                ExcerptRange {
11596                    context: Point::new(0, 0)..Point::new(3, 0),
11597                    primary: None,
11598                },
11599                ExcerptRange {
11600                    context: Point::new(5, 0)..Point::new(7, 0),
11601                    primary: None,
11602                },
11603                ExcerptRange {
11604                    context: Point::new(9, 0)..Point::new(10, 4),
11605                    primary: None,
11606                },
11607            ],
11608            cx,
11609        );
11610        multibuffer
11611    });
11612
11613    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11614    editor.update(cx, |editor, cx| {
11615        for (buffer, diff_base) in [
11616            (buffer_1.clone(), base_text_1),
11617            (buffer_2.clone(), base_text_2),
11618            (buffer_3.clone(), base_text_3),
11619        ] {
11620            let change_set = cx.new_model(|cx| {
11621                BufferChangeSet::new_with_base_text(
11622                    diff_base.to_string(),
11623                    buffer.read(cx).text_snapshot(),
11624                    cx,
11625                )
11626            });
11627            editor.diff_map.add_change_set(change_set, cx)
11628        }
11629    });
11630    cx.executor().run_until_parked();
11631
11632    editor.update(cx, |editor, cx| {
11633        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}");
11634        editor.select_all(&SelectAll, cx);
11635        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11636    });
11637    cx.executor().run_until_parked();
11638
11639    // When all ranges are selected, all buffer hunks are reverted.
11640    editor.update(cx, |editor, cx| {
11641        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");
11642    });
11643    buffer_1.update(cx, |buffer, _| {
11644        assert_eq!(buffer.text(), base_text_1);
11645    });
11646    buffer_2.update(cx, |buffer, _| {
11647        assert_eq!(buffer.text(), base_text_2);
11648    });
11649    buffer_3.update(cx, |buffer, _| {
11650        assert_eq!(buffer.text(), base_text_3);
11651    });
11652
11653    editor.update(cx, |editor, cx| {
11654        editor.undo(&Default::default(), cx);
11655    });
11656
11657    editor.update(cx, |editor, cx| {
11658        editor.change_selections(None, cx, |s| {
11659            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11660        });
11661        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11662    });
11663
11664    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11665    // but not affect buffer_2 and its related excerpts.
11666    editor.update(cx, |editor, cx| {
11667        assert_eq!(
11668            editor.text(cx),
11669            "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}"
11670        );
11671    });
11672    buffer_1.update(cx, |buffer, _| {
11673        assert_eq!(buffer.text(), base_text_1);
11674    });
11675    buffer_2.update(cx, |buffer, _| {
11676        assert_eq!(
11677            buffer.text(),
11678            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11679        );
11680    });
11681    buffer_3.update(cx, |buffer, _| {
11682        assert_eq!(
11683            buffer.text(),
11684            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11685        );
11686    });
11687
11688    fn edit_first_char_of_every_line(text: &str) -> String {
11689        text.split('\n')
11690            .map(|line| format!("X{}", &line[1..]))
11691            .collect::<Vec<_>>()
11692            .join("\n")
11693    }
11694}
11695
11696#[gpui::test]
11697async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11698    init_test(cx, |_| {});
11699
11700    let cols = 4;
11701    let rows = 10;
11702    let sample_text_1 = sample_text(rows, cols, 'a');
11703    assert_eq!(
11704        sample_text_1,
11705        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11706    );
11707    let sample_text_2 = sample_text(rows, cols, 'l');
11708    assert_eq!(
11709        sample_text_2,
11710        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11711    );
11712    let sample_text_3 = sample_text(rows, cols, 'v');
11713    assert_eq!(
11714        sample_text_3,
11715        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11716    );
11717
11718    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11719    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11720    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11721
11722    let multi_buffer = cx.new_model(|cx| {
11723        let mut multibuffer = MultiBuffer::new(ReadWrite);
11724        multibuffer.push_excerpts(
11725            buffer_1.clone(),
11726            [
11727                ExcerptRange {
11728                    context: Point::new(0, 0)..Point::new(3, 0),
11729                    primary: None,
11730                },
11731                ExcerptRange {
11732                    context: Point::new(5, 0)..Point::new(7, 0),
11733                    primary: None,
11734                },
11735                ExcerptRange {
11736                    context: Point::new(9, 0)..Point::new(10, 4),
11737                    primary: None,
11738                },
11739            ],
11740            cx,
11741        );
11742        multibuffer.push_excerpts(
11743            buffer_2.clone(),
11744            [
11745                ExcerptRange {
11746                    context: Point::new(0, 0)..Point::new(3, 0),
11747                    primary: None,
11748                },
11749                ExcerptRange {
11750                    context: Point::new(5, 0)..Point::new(7, 0),
11751                    primary: None,
11752                },
11753                ExcerptRange {
11754                    context: Point::new(9, 0)..Point::new(10, 4),
11755                    primary: None,
11756                },
11757            ],
11758            cx,
11759        );
11760        multibuffer.push_excerpts(
11761            buffer_3.clone(),
11762            [
11763                ExcerptRange {
11764                    context: Point::new(0, 0)..Point::new(3, 0),
11765                    primary: None,
11766                },
11767                ExcerptRange {
11768                    context: Point::new(5, 0)..Point::new(7, 0),
11769                    primary: None,
11770                },
11771                ExcerptRange {
11772                    context: Point::new(9, 0)..Point::new(10, 4),
11773                    primary: None,
11774                },
11775            ],
11776            cx,
11777        );
11778        multibuffer
11779    });
11780
11781    let fs = FakeFs::new(cx.executor());
11782    fs.insert_tree(
11783        "/a",
11784        json!({
11785            "main.rs": sample_text_1,
11786            "other.rs": sample_text_2,
11787            "lib.rs": sample_text_3,
11788        }),
11789    )
11790    .await;
11791    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11792    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11793    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11794    let multi_buffer_editor = cx.new_view(|cx| {
11795        Editor::new(
11796            EditorMode::Full,
11797            multi_buffer,
11798            Some(project.clone()),
11799            true,
11800            cx,
11801        )
11802    });
11803    let multibuffer_item_id = workspace
11804        .update(cx, |workspace, cx| {
11805            assert!(
11806                workspace.active_item(cx).is_none(),
11807                "active item should be None before the first item is added"
11808            );
11809            workspace.add_item_to_active_pane(
11810                Box::new(multi_buffer_editor.clone()),
11811                None,
11812                true,
11813                cx,
11814            );
11815            let active_item = workspace
11816                .active_item(cx)
11817                .expect("should have an active item after adding the multi buffer");
11818            assert!(
11819                !active_item.is_singleton(cx),
11820                "A multi buffer was expected to active after adding"
11821            );
11822            active_item.item_id()
11823        })
11824        .unwrap();
11825    cx.executor().run_until_parked();
11826
11827    multi_buffer_editor.update(cx, |editor, cx| {
11828        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11829        editor.open_excerpts(&OpenExcerpts, cx);
11830    });
11831    cx.executor().run_until_parked();
11832    let first_item_id = workspace
11833        .update(cx, |workspace, cx| {
11834            let active_item = workspace
11835                .active_item(cx)
11836                .expect("should have an active item after navigating into the 1st buffer");
11837            let first_item_id = active_item.item_id();
11838            assert_ne!(
11839                first_item_id, multibuffer_item_id,
11840                "Should navigate into the 1st buffer and activate it"
11841            );
11842            assert!(
11843                active_item.is_singleton(cx),
11844                "New active item should be a singleton buffer"
11845            );
11846            assert_eq!(
11847                active_item
11848                    .act_as::<Editor>(cx)
11849                    .expect("should have navigated into an editor for the 1st buffer")
11850                    .read(cx)
11851                    .text(cx),
11852                sample_text_1
11853            );
11854
11855            workspace
11856                .go_back(workspace.active_pane().downgrade(), cx)
11857                .detach_and_log_err(cx);
11858
11859            first_item_id
11860        })
11861        .unwrap();
11862    cx.executor().run_until_parked();
11863    workspace
11864        .update(cx, |workspace, cx| {
11865            let active_item = workspace
11866                .active_item(cx)
11867                .expect("should have an active item after navigating back");
11868            assert_eq!(
11869                active_item.item_id(),
11870                multibuffer_item_id,
11871                "Should navigate back to the multi buffer"
11872            );
11873            assert!(!active_item.is_singleton(cx));
11874        })
11875        .unwrap();
11876
11877    multi_buffer_editor.update(cx, |editor, cx| {
11878        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11879            s.select_ranges(Some(39..40))
11880        });
11881        editor.open_excerpts(&OpenExcerpts, cx);
11882    });
11883    cx.executor().run_until_parked();
11884    let second_item_id = workspace
11885        .update(cx, |workspace, cx| {
11886            let active_item = workspace
11887                .active_item(cx)
11888                .expect("should have an active item after navigating into the 2nd buffer");
11889            let second_item_id = active_item.item_id();
11890            assert_ne!(
11891                second_item_id, multibuffer_item_id,
11892                "Should navigate away from the multibuffer"
11893            );
11894            assert_ne!(
11895                second_item_id, first_item_id,
11896                "Should navigate into the 2nd buffer and activate it"
11897            );
11898            assert!(
11899                active_item.is_singleton(cx),
11900                "New active item should be a singleton buffer"
11901            );
11902            assert_eq!(
11903                active_item
11904                    .act_as::<Editor>(cx)
11905                    .expect("should have navigated into an editor")
11906                    .read(cx)
11907                    .text(cx),
11908                sample_text_2
11909            );
11910
11911            workspace
11912                .go_back(workspace.active_pane().downgrade(), cx)
11913                .detach_and_log_err(cx);
11914
11915            second_item_id
11916        })
11917        .unwrap();
11918    cx.executor().run_until_parked();
11919    workspace
11920        .update(cx, |workspace, cx| {
11921            let active_item = workspace
11922                .active_item(cx)
11923                .expect("should have an active item after navigating back from the 2nd buffer");
11924            assert_eq!(
11925                active_item.item_id(),
11926                multibuffer_item_id,
11927                "Should navigate back from the 2nd buffer to the multi buffer"
11928            );
11929            assert!(!active_item.is_singleton(cx));
11930        })
11931        .unwrap();
11932
11933    multi_buffer_editor.update(cx, |editor, cx| {
11934        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11935            s.select_ranges(Some(70..70))
11936        });
11937        editor.open_excerpts(&OpenExcerpts, cx);
11938    });
11939    cx.executor().run_until_parked();
11940    workspace
11941        .update(cx, |workspace, cx| {
11942            let active_item = workspace
11943                .active_item(cx)
11944                .expect("should have an active item after navigating into the 3rd buffer");
11945            let third_item_id = active_item.item_id();
11946            assert_ne!(
11947                third_item_id, multibuffer_item_id,
11948                "Should navigate into the 3rd buffer and activate it"
11949            );
11950            assert_ne!(third_item_id, first_item_id);
11951            assert_ne!(third_item_id, second_item_id);
11952            assert!(
11953                active_item.is_singleton(cx),
11954                "New active item should be a singleton buffer"
11955            );
11956            assert_eq!(
11957                active_item
11958                    .act_as::<Editor>(cx)
11959                    .expect("should have navigated into an editor")
11960                    .read(cx)
11961                    .text(cx),
11962                sample_text_3
11963            );
11964
11965            workspace
11966                .go_back(workspace.active_pane().downgrade(), cx)
11967                .detach_and_log_err(cx);
11968        })
11969        .unwrap();
11970    cx.executor().run_until_parked();
11971    workspace
11972        .update(cx, |workspace, cx| {
11973            let active_item = workspace
11974                .active_item(cx)
11975                .expect("should have an active item after navigating back from the 3rd buffer");
11976            assert_eq!(
11977                active_item.item_id(),
11978                multibuffer_item_id,
11979                "Should navigate back from the 3rd buffer to the multi buffer"
11980            );
11981            assert!(!active_item.is_singleton(cx));
11982        })
11983        .unwrap();
11984}
11985
11986#[gpui::test]
11987async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11988    init_test(cx, |_| {});
11989
11990    let mut cx = EditorTestContext::new(cx).await;
11991
11992    let diff_base = r#"
11993        use some::mod;
11994
11995        const A: u32 = 42;
11996
11997        fn main() {
11998            println!("hello");
11999
12000            println!("world");
12001        }
12002        "#
12003    .unindent();
12004
12005    cx.set_state(
12006        &r#"
12007        use some::modified;
12008
12009        ˇ
12010        fn main() {
12011            println!("hello there");
12012
12013            println!("around the");
12014            println!("world");
12015        }
12016        "#
12017        .unindent(),
12018    );
12019
12020    cx.set_diff_base(&diff_base);
12021    executor.run_until_parked();
12022
12023    cx.update_editor(|editor, cx| {
12024        editor.go_to_next_hunk(&GoToHunk, cx);
12025        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12026    });
12027    executor.run_until_parked();
12028    cx.assert_state_with_diff(
12029        r#"
12030          use some::modified;
12031
12032
12033          fn main() {
12034        -     println!("hello");
12035        + ˇ    println!("hello there");
12036
12037              println!("around the");
12038              println!("world");
12039          }
12040        "#
12041        .unindent(),
12042    );
12043
12044    cx.update_editor(|editor, cx| {
12045        for _ in 0..3 {
12046            editor.go_to_next_hunk(&GoToHunk, cx);
12047            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12048        }
12049    });
12050    executor.run_until_parked();
12051    cx.assert_state_with_diff(
12052        r#"
12053        - use some::mod;
12054        + use some::modified;
12055
12056        - const A: u32 = 42;
12057          ˇ
12058          fn main() {
12059        -     println!("hello");
12060        +     println!("hello there");
12061
12062        +     println!("around the");
12063              println!("world");
12064          }
12065        "#
12066        .unindent(),
12067    );
12068
12069    cx.update_editor(|editor, cx| {
12070        editor.cancel(&Cancel, cx);
12071    });
12072
12073    cx.assert_state_with_diff(
12074        r#"
12075          use some::modified;
12076
12077          ˇ
12078          fn main() {
12079              println!("hello there");
12080
12081              println!("around the");
12082              println!("world");
12083          }
12084        "#
12085        .unindent(),
12086    );
12087}
12088
12089#[gpui::test]
12090async fn test_diff_base_change_with_expanded_diff_hunks(
12091    executor: BackgroundExecutor,
12092    cx: &mut gpui::TestAppContext,
12093) {
12094    init_test(cx, |_| {});
12095
12096    let mut cx = EditorTestContext::new(cx).await;
12097
12098    let diff_base = r#"
12099        use some::mod1;
12100        use some::mod2;
12101
12102        const A: u32 = 42;
12103        const B: u32 = 42;
12104        const C: u32 = 42;
12105
12106        fn main() {
12107            println!("hello");
12108
12109            println!("world");
12110        }
12111        "#
12112    .unindent();
12113
12114    cx.set_state(
12115        &r#"
12116        use some::mod2;
12117
12118        const A: u32 = 42;
12119        const C: u32 = 42;
12120
12121        fn main(ˇ) {
12122            //println!("hello");
12123
12124            println!("world");
12125            //
12126            //
12127        }
12128        "#
12129        .unindent(),
12130    );
12131
12132    cx.set_diff_base(&diff_base);
12133    executor.run_until_parked();
12134
12135    cx.update_editor(|editor, cx| {
12136        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12137    });
12138    executor.run_until_parked();
12139    cx.assert_state_with_diff(
12140        r#"
12141        - use some::mod1;
12142          use some::mod2;
12143
12144          const A: u32 = 42;
12145        - const B: u32 = 42;
12146          const C: u32 = 42;
12147
12148          fn main(ˇ) {
12149        -     println!("hello");
12150        +     //println!("hello");
12151
12152              println!("world");
12153        +     //
12154        +     //
12155          }
12156        "#
12157        .unindent(),
12158    );
12159
12160    cx.set_diff_base("new diff base!");
12161    executor.run_until_parked();
12162    cx.assert_state_with_diff(
12163        r#"
12164          use some::mod2;
12165
12166          const A: u32 = 42;
12167          const C: u32 = 42;
12168
12169          fn main(ˇ) {
12170              //println!("hello");
12171
12172              println!("world");
12173              //
12174              //
12175          }
12176        "#
12177        .unindent(),
12178    );
12179
12180    cx.update_editor(|editor, cx| {
12181        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12182    });
12183    executor.run_until_parked();
12184    cx.assert_state_with_diff(
12185        r#"
12186        - new diff base!
12187        + use some::mod2;
12188        +
12189        + const A: u32 = 42;
12190        + const C: u32 = 42;
12191        +
12192        + fn main(ˇ) {
12193        +     //println!("hello");
12194        +
12195        +     println!("world");
12196        +     //
12197        +     //
12198        + }
12199        "#
12200        .unindent(),
12201    );
12202}
12203
12204#[gpui::test]
12205async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12206    init_test(cx, |_| {});
12207
12208    let mut cx = EditorTestContext::new(cx).await;
12209
12210    let diff_base = r#"
12211        use some::mod1;
12212        use some::mod2;
12213
12214        const A: u32 = 42;
12215        const B: u32 = 42;
12216        const C: u32 = 42;
12217
12218        fn main() {
12219            println!("hello");
12220
12221            println!("world");
12222        }
12223
12224        fn another() {
12225            println!("another");
12226        }
12227
12228        fn another2() {
12229            println!("another2");
12230        }
12231        "#
12232    .unindent();
12233
12234    cx.set_state(
12235        &r#"
12236        «use some::mod2;
12237
12238        const A: u32 = 42;
12239        const C: u32 = 42;
12240
12241        fn main() {
12242            //println!("hello");
12243
12244            println!("world");
12245            //
12246            //ˇ»
12247        }
12248
12249        fn another() {
12250            println!("another");
12251            println!("another");
12252        }
12253
12254            println!("another2");
12255        }
12256        "#
12257        .unindent(),
12258    );
12259
12260    cx.set_diff_base(&diff_base);
12261    executor.run_until_parked();
12262
12263    cx.update_editor(|editor, cx| {
12264        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12265    });
12266    executor.run_until_parked();
12267
12268    cx.assert_state_with_diff(
12269        r#"
12270        - use some::mod1;
12271          «use some::mod2;
12272
12273          const A: u32 = 42;
12274        - const B: u32 = 42;
12275          const C: u32 = 42;
12276
12277          fn main() {
12278        -     println!("hello");
12279        +     //println!("hello");
12280
12281              println!("world");
12282        +     //
12283        +     //ˇ»
12284          }
12285
12286          fn another() {
12287              println!("another");
12288        +     println!("another");
12289          }
12290
12291        - fn another2() {
12292              println!("another2");
12293          }
12294        "#
12295        .unindent(),
12296    );
12297
12298    // Fold across some of the diff hunks. They should no longer appear expanded.
12299    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12300    cx.executor().run_until_parked();
12301
12302    // Hunks are not shown if their position is within a fold
12303    cx.assert_state_with_diff(
12304        r#"
12305          «use some::mod2;
12306
12307          const A: u32 = 42;
12308          const C: u32 = 42;
12309
12310          fn main() {
12311              //println!("hello");
12312
12313              println!("world");
12314              //
12315              //ˇ»
12316          }
12317
12318          fn another() {
12319              println!("another");
12320        +     println!("another");
12321          }
12322
12323        - fn another2() {
12324              println!("another2");
12325          }
12326        "#
12327        .unindent(),
12328    );
12329
12330    cx.update_editor(|editor, cx| {
12331        editor.select_all(&SelectAll, cx);
12332        editor.unfold_lines(&UnfoldLines, cx);
12333    });
12334    cx.executor().run_until_parked();
12335
12336    // The deletions reappear when unfolding.
12337    cx.assert_state_with_diff(
12338        r#"
12339        - use some::mod1;
12340          «use some::mod2;
12341
12342          const A: u32 = 42;
12343        - const B: u32 = 42;
12344          const C: u32 = 42;
12345
12346          fn main() {
12347        -     println!("hello");
12348        +     //println!("hello");
12349
12350              println!("world");
12351        +     //
12352        +     //
12353          }
12354
12355          fn another() {
12356              println!("another");
12357        +     println!("another");
12358          }
12359
12360        - fn another2() {
12361              println!("another2");
12362          }
12363          ˇ»"#
12364        .unindent(),
12365    );
12366}
12367
12368#[gpui::test]
12369async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12370    init_test(cx, |_| {});
12371
12372    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12373    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12374    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12375    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12376    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12377    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12378
12379    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12380    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12381    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12382
12383    let multi_buffer = cx.new_model(|cx| {
12384        let mut multibuffer = MultiBuffer::new(ReadWrite);
12385        multibuffer.push_excerpts(
12386            buffer_1.clone(),
12387            [
12388                ExcerptRange {
12389                    context: Point::new(0, 0)..Point::new(3, 0),
12390                    primary: None,
12391                },
12392                ExcerptRange {
12393                    context: Point::new(5, 0)..Point::new(7, 0),
12394                    primary: None,
12395                },
12396                ExcerptRange {
12397                    context: Point::new(9, 0)..Point::new(10, 3),
12398                    primary: None,
12399                },
12400            ],
12401            cx,
12402        );
12403        multibuffer.push_excerpts(
12404            buffer_2.clone(),
12405            [
12406                ExcerptRange {
12407                    context: Point::new(0, 0)..Point::new(3, 0),
12408                    primary: None,
12409                },
12410                ExcerptRange {
12411                    context: Point::new(5, 0)..Point::new(7, 0),
12412                    primary: None,
12413                },
12414                ExcerptRange {
12415                    context: Point::new(9, 0)..Point::new(10, 3),
12416                    primary: None,
12417                },
12418            ],
12419            cx,
12420        );
12421        multibuffer.push_excerpts(
12422            buffer_3.clone(),
12423            [
12424                ExcerptRange {
12425                    context: Point::new(0, 0)..Point::new(3, 0),
12426                    primary: None,
12427                },
12428                ExcerptRange {
12429                    context: Point::new(5, 0)..Point::new(7, 0),
12430                    primary: None,
12431                },
12432                ExcerptRange {
12433                    context: Point::new(9, 0)..Point::new(10, 3),
12434                    primary: None,
12435                },
12436            ],
12437            cx,
12438        );
12439        multibuffer
12440    });
12441
12442    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12443    editor
12444        .update(cx, |editor, cx| {
12445            for (buffer, diff_base) in [
12446                (buffer_1.clone(), file_1_old),
12447                (buffer_2.clone(), file_2_old),
12448                (buffer_3.clone(), file_3_old),
12449            ] {
12450                let change_set = cx.new_model(|cx| {
12451                    BufferChangeSet::new_with_base_text(
12452                        diff_base.to_string(),
12453                        buffer.read(cx).text_snapshot(),
12454                        cx,
12455                    )
12456                });
12457                editor.diff_map.add_change_set(change_set, cx)
12458            }
12459        })
12460        .unwrap();
12461
12462    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12463    cx.run_until_parked();
12464
12465    cx.assert_editor_state(
12466        &"
12467            ˇaaa
12468            ccc
12469            ddd
12470
12471            ggg
12472            hhh
12473
12474
12475            lll
12476            mmm
12477            NNN
12478
12479            qqq
12480            rrr
12481
12482            uuu
12483            111
12484            222
12485            333
12486
12487            666
12488            777
12489
12490            000
12491            !!!"
12492        .unindent(),
12493    );
12494
12495    cx.update_editor(|editor, cx| {
12496        editor.select_all(&SelectAll, cx);
12497        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12498    });
12499    cx.executor().run_until_parked();
12500
12501    cx.assert_state_with_diff(
12502        "
12503            «aaa
12504          - bbb
12505            ccc
12506            ddd
12507
12508            ggg
12509            hhh
12510
12511
12512            lll
12513            mmm
12514          - nnn
12515          + NNN
12516
12517            qqq
12518            rrr
12519
12520            uuu
12521            111
12522            222
12523            333
12524
12525          + 666
12526            777
12527
12528            000
12529            !!!ˇ»"
12530            .unindent(),
12531    );
12532}
12533
12534#[gpui::test]
12535async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12536    init_test(cx, |_| {});
12537
12538    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12539    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12540
12541    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12542    let multi_buffer = cx.new_model(|cx| {
12543        let mut multibuffer = MultiBuffer::new(ReadWrite);
12544        multibuffer.push_excerpts(
12545            buffer.clone(),
12546            [
12547                ExcerptRange {
12548                    context: Point::new(0, 0)..Point::new(2, 0),
12549                    primary: None,
12550                },
12551                ExcerptRange {
12552                    context: Point::new(5, 0)..Point::new(7, 0),
12553                    primary: None,
12554                },
12555            ],
12556            cx,
12557        );
12558        multibuffer
12559    });
12560
12561    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12562    editor
12563        .update(cx, |editor, cx| {
12564            let buffer = buffer.read(cx).text_snapshot();
12565            let change_set = cx
12566                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12567            editor.diff_map.add_change_set(change_set, cx)
12568        })
12569        .unwrap();
12570
12571    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12572    cx.run_until_parked();
12573
12574    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12575    cx.executor().run_until_parked();
12576
12577    cx.assert_state_with_diff(
12578        "
12579            ˇaaa
12580          - bbb
12581          + BBB
12582
12583          - ddd
12584          - eee
12585          + EEE
12586            fff
12587        "
12588        .unindent(),
12589    );
12590}
12591
12592#[gpui::test]
12593async fn test_edits_around_expanded_insertion_hunks(
12594    executor: BackgroundExecutor,
12595    cx: &mut gpui::TestAppContext,
12596) {
12597    init_test(cx, |_| {});
12598
12599    let mut cx = EditorTestContext::new(cx).await;
12600
12601    let diff_base = r#"
12602        use some::mod1;
12603        use some::mod2;
12604
12605        const A: u32 = 42;
12606
12607        fn main() {
12608            println!("hello");
12609
12610            println!("world");
12611        }
12612        "#
12613    .unindent();
12614    executor.run_until_parked();
12615    cx.set_state(
12616        &r#"
12617        use some::mod1;
12618        use some::mod2;
12619
12620        const A: u32 = 42;
12621        const B: u32 = 42;
12622        const C: u32 = 42;
12623        ˇ
12624
12625        fn main() {
12626            println!("hello");
12627
12628            println!("world");
12629        }
12630        "#
12631        .unindent(),
12632    );
12633
12634    cx.set_diff_base(&diff_base);
12635    executor.run_until_parked();
12636
12637    cx.update_editor(|editor, cx| {
12638        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12639    });
12640    executor.run_until_parked();
12641
12642    cx.assert_state_with_diff(
12643        r#"
12644        use some::mod1;
12645        use some::mod2;
12646
12647        const A: u32 = 42;
12648      + const B: u32 = 42;
12649      + const C: u32 = 42;
12650      + ˇ
12651
12652        fn main() {
12653            println!("hello");
12654
12655            println!("world");
12656        }
12657        "#
12658        .unindent(),
12659    );
12660
12661    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12662    executor.run_until_parked();
12663
12664    cx.assert_state_with_diff(
12665        r#"
12666        use some::mod1;
12667        use some::mod2;
12668
12669        const A: u32 = 42;
12670      + const B: u32 = 42;
12671      + const C: u32 = 42;
12672      + const D: u32 = 42;
12673      + ˇ
12674
12675        fn main() {
12676            println!("hello");
12677
12678            println!("world");
12679        }
12680        "#
12681        .unindent(),
12682    );
12683
12684    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12685    executor.run_until_parked();
12686
12687    cx.assert_state_with_diff(
12688        r#"
12689        use some::mod1;
12690        use some::mod2;
12691
12692        const A: u32 = 42;
12693      + const B: u32 = 42;
12694      + const C: u32 = 42;
12695      + const D: u32 = 42;
12696      + const E: u32 = 42;
12697      + ˇ
12698
12699        fn main() {
12700            println!("hello");
12701
12702            println!("world");
12703        }
12704        "#
12705        .unindent(),
12706    );
12707
12708    cx.update_editor(|editor, cx| {
12709        editor.delete_line(&DeleteLine, cx);
12710    });
12711    executor.run_until_parked();
12712
12713    cx.assert_state_with_diff(
12714        r#"
12715        use some::mod1;
12716        use some::mod2;
12717
12718        const A: u32 = 42;
12719      + const B: u32 = 42;
12720      + const C: u32 = 42;
12721      + const D: u32 = 42;
12722      + const E: u32 = 42;
12723        ˇ
12724        fn main() {
12725            println!("hello");
12726
12727            println!("world");
12728        }
12729        "#
12730        .unindent(),
12731    );
12732
12733    cx.update_editor(|editor, cx| {
12734        editor.move_up(&MoveUp, cx);
12735        editor.delete_line(&DeleteLine, cx);
12736        editor.move_up(&MoveUp, cx);
12737        editor.delete_line(&DeleteLine, cx);
12738        editor.move_up(&MoveUp, cx);
12739        editor.delete_line(&DeleteLine, cx);
12740    });
12741    executor.run_until_parked();
12742    cx.assert_state_with_diff(
12743        r#"
12744        use some::mod1;
12745        use some::mod2;
12746
12747        const A: u32 = 42;
12748      + const B: u32 = 42;
12749        ˇ
12750        fn main() {
12751            println!("hello");
12752
12753            println!("world");
12754        }
12755        "#
12756        .unindent(),
12757    );
12758
12759    cx.update_editor(|editor, cx| {
12760        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12761        editor.delete_line(&DeleteLine, cx);
12762    });
12763    executor.run_until_parked();
12764    cx.assert_state_with_diff(
12765        r#"
12766        use some::mod1;
12767      - use some::mod2;
12768      -
12769      - const A: u32 = 42;
12770        ˇ
12771        fn main() {
12772            println!("hello");
12773
12774            println!("world");
12775        }
12776        "#
12777        .unindent(),
12778    );
12779}
12780
12781#[gpui::test]
12782async fn test_edits_around_expanded_deletion_hunks(
12783    executor: BackgroundExecutor,
12784    cx: &mut gpui::TestAppContext,
12785) {
12786    init_test(cx, |_| {});
12787
12788    let mut cx = EditorTestContext::new(cx).await;
12789
12790    let diff_base = r#"
12791        use some::mod1;
12792        use some::mod2;
12793
12794        const A: u32 = 42;
12795        const B: u32 = 42;
12796        const C: u32 = 42;
12797
12798
12799        fn main() {
12800            println!("hello");
12801
12802            println!("world");
12803        }
12804    "#
12805    .unindent();
12806    executor.run_until_parked();
12807    cx.set_state(
12808        &r#"
12809        use some::mod1;
12810        use some::mod2;
12811
12812        ˇconst B: u32 = 42;
12813        const C: u32 = 42;
12814
12815
12816        fn main() {
12817            println!("hello");
12818
12819            println!("world");
12820        }
12821        "#
12822        .unindent(),
12823    );
12824
12825    cx.set_diff_base(&diff_base);
12826    executor.run_until_parked();
12827
12828    cx.update_editor(|editor, cx| {
12829        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12830    });
12831    executor.run_until_parked();
12832
12833    cx.assert_state_with_diff(
12834        r#"
12835        use some::mod1;
12836        use some::mod2;
12837
12838      - const A: u32 = 42;
12839        ˇconst B: u32 = 42;
12840        const C: u32 = 42;
12841
12842
12843        fn main() {
12844            println!("hello");
12845
12846            println!("world");
12847        }
12848        "#
12849        .unindent(),
12850    );
12851
12852    cx.update_editor(|editor, cx| {
12853        editor.delete_line(&DeleteLine, cx);
12854    });
12855    executor.run_until_parked();
12856    cx.assert_state_with_diff(
12857        r#"
12858        use some::mod1;
12859        use some::mod2;
12860
12861      - const A: u32 = 42;
12862      - const B: u32 = 42;
12863        ˇconst C: u32 = 42;
12864
12865
12866        fn main() {
12867            println!("hello");
12868
12869            println!("world");
12870        }
12871        "#
12872        .unindent(),
12873    );
12874
12875    cx.update_editor(|editor, cx| {
12876        editor.delete_line(&DeleteLine, cx);
12877    });
12878    executor.run_until_parked();
12879    cx.assert_state_with_diff(
12880        r#"
12881        use some::mod1;
12882        use some::mod2;
12883
12884      - const A: u32 = 42;
12885      - const B: u32 = 42;
12886      - const C: u32 = 42;
12887        ˇ
12888
12889        fn main() {
12890            println!("hello");
12891
12892            println!("world");
12893        }
12894        "#
12895        .unindent(),
12896    );
12897
12898    cx.update_editor(|editor, cx| {
12899        editor.handle_input("replacement", cx);
12900    });
12901    executor.run_until_parked();
12902    cx.assert_state_with_diff(
12903        r#"
12904        use some::mod1;
12905        use some::mod2;
12906
12907      - const A: u32 = 42;
12908      - const B: u32 = 42;
12909      - const C: u32 = 42;
12910      -
12911      + replacementˇ
12912
12913        fn main() {
12914            println!("hello");
12915
12916            println!("world");
12917        }
12918        "#
12919        .unindent(),
12920    );
12921}
12922
12923#[gpui::test]
12924async fn test_edit_after_expanded_modification_hunk(
12925    executor: BackgroundExecutor,
12926    cx: &mut gpui::TestAppContext,
12927) {
12928    init_test(cx, |_| {});
12929
12930    let mut cx = EditorTestContext::new(cx).await;
12931
12932    let diff_base = r#"
12933        use some::mod1;
12934        use some::mod2;
12935
12936        const A: u32 = 42;
12937        const B: u32 = 42;
12938        const C: u32 = 42;
12939        const D: u32 = 42;
12940
12941
12942        fn main() {
12943            println!("hello");
12944
12945            println!("world");
12946        }"#
12947    .unindent();
12948
12949    cx.set_state(
12950        &r#"
12951        use some::mod1;
12952        use some::mod2;
12953
12954        const A: u32 = 42;
12955        const B: u32 = 42;
12956        const C: u32 = 43ˇ
12957        const D: u32 = 42;
12958
12959
12960        fn main() {
12961            println!("hello");
12962
12963            println!("world");
12964        }"#
12965        .unindent(),
12966    );
12967
12968    cx.set_diff_base(&diff_base);
12969    executor.run_until_parked();
12970    cx.update_editor(|editor, cx| {
12971        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12972    });
12973    executor.run_until_parked();
12974
12975    cx.assert_state_with_diff(
12976        r#"
12977        use some::mod1;
12978        use some::mod2;
12979
12980        const A: u32 = 42;
12981        const B: u32 = 42;
12982      - const C: u32 = 42;
12983      + const C: u32 = 43ˇ
12984        const D: u32 = 42;
12985
12986
12987        fn main() {
12988            println!("hello");
12989
12990            println!("world");
12991        }"#
12992        .unindent(),
12993    );
12994
12995    cx.update_editor(|editor, cx| {
12996        editor.handle_input("\nnew_line\n", cx);
12997    });
12998    executor.run_until_parked();
12999
13000    cx.assert_state_with_diff(
13001        r#"
13002        use some::mod1;
13003        use some::mod2;
13004
13005        const A: u32 = 42;
13006        const B: u32 = 42;
13007      - const C: u32 = 42;
13008      + const C: u32 = 43
13009      + new_line
13010      + ˇ
13011        const D: u32 = 42;
13012
13013
13014        fn main() {
13015            println!("hello");
13016
13017            println!("world");
13018        }"#
13019        .unindent(),
13020    );
13021}
13022
13023async fn setup_indent_guides_editor(
13024    text: &str,
13025    cx: &mut gpui::TestAppContext,
13026) -> (BufferId, EditorTestContext) {
13027    init_test(cx, |_| {});
13028
13029    let mut cx = EditorTestContext::new(cx).await;
13030
13031    let buffer_id = cx.update_editor(|editor, cx| {
13032        editor.set_text(text, cx);
13033        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13034
13035        buffer_ids[0]
13036    });
13037
13038    (buffer_id, cx)
13039}
13040
13041fn assert_indent_guides(
13042    range: Range<u32>,
13043    expected: Vec<IndentGuide>,
13044    active_indices: Option<Vec<usize>>,
13045    cx: &mut EditorTestContext,
13046) {
13047    let indent_guides = cx.update_editor(|editor, cx| {
13048        let snapshot = editor.snapshot(cx).display_snapshot;
13049        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13050            editor,
13051            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13052            true,
13053            &snapshot,
13054            cx,
13055        );
13056
13057        indent_guides.sort_by(|a, b| {
13058            a.depth.cmp(&b.depth).then(
13059                a.start_row
13060                    .cmp(&b.start_row)
13061                    .then(a.end_row.cmp(&b.end_row)),
13062            )
13063        });
13064        indent_guides
13065    });
13066
13067    if let Some(expected) = active_indices {
13068        let active_indices = cx.update_editor(|editor, cx| {
13069            let snapshot = editor.snapshot(cx).display_snapshot;
13070            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13071        });
13072
13073        assert_eq!(
13074            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13075            expected,
13076            "Active indent guide indices do not match"
13077        );
13078    }
13079
13080    let expected: Vec<_> = expected
13081        .into_iter()
13082        .map(|guide| MultiBufferIndentGuide {
13083            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13084            buffer: guide,
13085        })
13086        .collect();
13087
13088    assert_eq!(indent_guides, expected, "Indent guides do not match");
13089}
13090
13091fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13092    IndentGuide {
13093        buffer_id,
13094        start_row,
13095        end_row,
13096        depth,
13097        tab_size: 4,
13098        settings: IndentGuideSettings {
13099            enabled: true,
13100            line_width: 1,
13101            active_line_width: 1,
13102            ..Default::default()
13103        },
13104    }
13105}
13106
13107#[gpui::test]
13108async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13109    let (buffer_id, mut cx) = setup_indent_guides_editor(
13110        &"
13111    fn main() {
13112        let a = 1;
13113    }"
13114        .unindent(),
13115        cx,
13116    )
13117    .await;
13118
13119    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13120}
13121
13122#[gpui::test]
13123async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13124    let (buffer_id, mut cx) = setup_indent_guides_editor(
13125        &"
13126    fn main() {
13127        let a = 1;
13128        let b = 2;
13129    }"
13130        .unindent(),
13131        cx,
13132    )
13133    .await;
13134
13135    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13136}
13137
13138#[gpui::test]
13139async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13140    let (buffer_id, mut cx) = setup_indent_guides_editor(
13141        &"
13142    fn main() {
13143        let a = 1;
13144        if a == 3 {
13145            let b = 2;
13146        } else {
13147            let c = 3;
13148        }
13149    }"
13150        .unindent(),
13151        cx,
13152    )
13153    .await;
13154
13155    assert_indent_guides(
13156        0..8,
13157        vec![
13158            indent_guide(buffer_id, 1, 6, 0),
13159            indent_guide(buffer_id, 3, 3, 1),
13160            indent_guide(buffer_id, 5, 5, 1),
13161        ],
13162        None,
13163        &mut cx,
13164    );
13165}
13166
13167#[gpui::test]
13168async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13169    let (buffer_id, mut cx) = setup_indent_guides_editor(
13170        &"
13171    fn main() {
13172        let a = 1;
13173            let b = 2;
13174        let c = 3;
13175    }"
13176        .unindent(),
13177        cx,
13178    )
13179    .await;
13180
13181    assert_indent_guides(
13182        0..5,
13183        vec![
13184            indent_guide(buffer_id, 1, 3, 0),
13185            indent_guide(buffer_id, 2, 2, 1),
13186        ],
13187        None,
13188        &mut cx,
13189    );
13190}
13191
13192#[gpui::test]
13193async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13194    let (buffer_id, mut cx) = setup_indent_guides_editor(
13195        &"
13196        fn main() {
13197            let a = 1;
13198
13199            let c = 3;
13200        }"
13201        .unindent(),
13202        cx,
13203    )
13204    .await;
13205
13206    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13207}
13208
13209#[gpui::test]
13210async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13211    let (buffer_id, mut cx) = setup_indent_guides_editor(
13212        &"
13213        fn main() {
13214            let a = 1;
13215
13216            let c = 3;
13217
13218            if a == 3 {
13219                let b = 2;
13220            } else {
13221                let c = 3;
13222            }
13223        }"
13224        .unindent(),
13225        cx,
13226    )
13227    .await;
13228
13229    assert_indent_guides(
13230        0..11,
13231        vec![
13232            indent_guide(buffer_id, 1, 9, 0),
13233            indent_guide(buffer_id, 6, 6, 1),
13234            indent_guide(buffer_id, 8, 8, 1),
13235        ],
13236        None,
13237        &mut cx,
13238    );
13239}
13240
13241#[gpui::test]
13242async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13243    let (buffer_id, mut cx) = setup_indent_guides_editor(
13244        &"
13245        fn main() {
13246            let a = 1;
13247
13248            let c = 3;
13249
13250            if a == 3 {
13251                let b = 2;
13252            } else {
13253                let c = 3;
13254            }
13255        }"
13256        .unindent(),
13257        cx,
13258    )
13259    .await;
13260
13261    assert_indent_guides(
13262        1..11,
13263        vec![
13264            indent_guide(buffer_id, 1, 9, 0),
13265            indent_guide(buffer_id, 6, 6, 1),
13266            indent_guide(buffer_id, 8, 8, 1),
13267        ],
13268        None,
13269        &mut cx,
13270    );
13271}
13272
13273#[gpui::test]
13274async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13275    let (buffer_id, mut cx) = setup_indent_guides_editor(
13276        &"
13277        fn main() {
13278            let a = 1;
13279
13280            let c = 3;
13281
13282            if a == 3 {
13283                let b = 2;
13284            } else {
13285                let c = 3;
13286            }
13287        }"
13288        .unindent(),
13289        cx,
13290    )
13291    .await;
13292
13293    assert_indent_guides(
13294        1..10,
13295        vec![
13296            indent_guide(buffer_id, 1, 9, 0),
13297            indent_guide(buffer_id, 6, 6, 1),
13298            indent_guide(buffer_id, 8, 8, 1),
13299        ],
13300        None,
13301        &mut cx,
13302    );
13303}
13304
13305#[gpui::test]
13306async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13307    let (buffer_id, mut cx) = setup_indent_guides_editor(
13308        &"
13309        block1
13310            block2
13311                block3
13312                    block4
13313            block2
13314        block1
13315        block1"
13316            .unindent(),
13317        cx,
13318    )
13319    .await;
13320
13321    assert_indent_guides(
13322        1..10,
13323        vec![
13324            indent_guide(buffer_id, 1, 4, 0),
13325            indent_guide(buffer_id, 2, 3, 1),
13326            indent_guide(buffer_id, 3, 3, 2),
13327        ],
13328        None,
13329        &mut cx,
13330    );
13331}
13332
13333#[gpui::test]
13334async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13335    let (buffer_id, mut cx) = setup_indent_guides_editor(
13336        &"
13337        block1
13338            block2
13339                block3
13340
13341        block1
13342        block1"
13343            .unindent(),
13344        cx,
13345    )
13346    .await;
13347
13348    assert_indent_guides(
13349        0..6,
13350        vec![
13351            indent_guide(buffer_id, 1, 2, 0),
13352            indent_guide(buffer_id, 2, 2, 1),
13353        ],
13354        None,
13355        &mut cx,
13356    );
13357}
13358
13359#[gpui::test]
13360async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13361    let (buffer_id, mut cx) = setup_indent_guides_editor(
13362        &"
13363        block1
13364
13365
13366
13367            block2
13368        "
13369        .unindent(),
13370        cx,
13371    )
13372    .await;
13373
13374    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13375}
13376
13377#[gpui::test]
13378async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13379    let (buffer_id, mut cx) = setup_indent_guides_editor(
13380        &"
13381        def a:
13382        \tb = 3
13383        \tif True:
13384        \t\tc = 4
13385        \t\td = 5
13386        \tprint(b)
13387        "
13388        .unindent(),
13389        cx,
13390    )
13391    .await;
13392
13393    assert_indent_guides(
13394        0..6,
13395        vec![
13396            indent_guide(buffer_id, 1, 6, 0),
13397            indent_guide(buffer_id, 3, 4, 1),
13398        ],
13399        None,
13400        &mut cx,
13401    );
13402}
13403
13404#[gpui::test]
13405async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13406    let (buffer_id, mut cx) = setup_indent_guides_editor(
13407        &"
13408    fn main() {
13409        let a = 1;
13410    }"
13411        .unindent(),
13412        cx,
13413    )
13414    .await;
13415
13416    cx.update_editor(|editor, cx| {
13417        editor.change_selections(None, cx, |s| {
13418            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13419        });
13420    });
13421
13422    assert_indent_guides(
13423        0..3,
13424        vec![indent_guide(buffer_id, 1, 1, 0)],
13425        Some(vec![0]),
13426        &mut cx,
13427    );
13428}
13429
13430#[gpui::test]
13431async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13432    let (buffer_id, mut cx) = setup_indent_guides_editor(
13433        &"
13434    fn main() {
13435        if 1 == 2 {
13436            let a = 1;
13437        }
13438    }"
13439        .unindent(),
13440        cx,
13441    )
13442    .await;
13443
13444    cx.update_editor(|editor, cx| {
13445        editor.change_selections(None, cx, |s| {
13446            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13447        });
13448    });
13449
13450    assert_indent_guides(
13451        0..4,
13452        vec![
13453            indent_guide(buffer_id, 1, 3, 0),
13454            indent_guide(buffer_id, 2, 2, 1),
13455        ],
13456        Some(vec![1]),
13457        &mut cx,
13458    );
13459
13460    cx.update_editor(|editor, cx| {
13461        editor.change_selections(None, cx, |s| {
13462            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13463        });
13464    });
13465
13466    assert_indent_guides(
13467        0..4,
13468        vec![
13469            indent_guide(buffer_id, 1, 3, 0),
13470            indent_guide(buffer_id, 2, 2, 1),
13471        ],
13472        Some(vec![1]),
13473        &mut cx,
13474    );
13475
13476    cx.update_editor(|editor, cx| {
13477        editor.change_selections(None, cx, |s| {
13478            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13479        });
13480    });
13481
13482    assert_indent_guides(
13483        0..4,
13484        vec![
13485            indent_guide(buffer_id, 1, 3, 0),
13486            indent_guide(buffer_id, 2, 2, 1),
13487        ],
13488        Some(vec![0]),
13489        &mut cx,
13490    );
13491}
13492
13493#[gpui::test]
13494async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13495    let (buffer_id, mut cx) = setup_indent_guides_editor(
13496        &"
13497    fn main() {
13498        let a = 1;
13499
13500        let b = 2;
13501    }"
13502        .unindent(),
13503        cx,
13504    )
13505    .await;
13506
13507    cx.update_editor(|editor, cx| {
13508        editor.change_selections(None, cx, |s| {
13509            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13510        });
13511    });
13512
13513    assert_indent_guides(
13514        0..5,
13515        vec![indent_guide(buffer_id, 1, 3, 0)],
13516        Some(vec![0]),
13517        &mut cx,
13518    );
13519}
13520
13521#[gpui::test]
13522async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13523    let (buffer_id, mut cx) = setup_indent_guides_editor(
13524        &"
13525    def m:
13526        a = 1
13527        pass"
13528            .unindent(),
13529        cx,
13530    )
13531    .await;
13532
13533    cx.update_editor(|editor, cx| {
13534        editor.change_selections(None, cx, |s| {
13535            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13536        });
13537    });
13538
13539    assert_indent_guides(
13540        0..3,
13541        vec![indent_guide(buffer_id, 1, 2, 0)],
13542        Some(vec![0]),
13543        &mut cx,
13544    );
13545}
13546
13547#[gpui::test]
13548fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13549    init_test(cx, |_| {});
13550
13551    let editor = cx.add_window(|cx| {
13552        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13553        build_editor(buffer, cx)
13554    });
13555
13556    let render_args = Arc::new(Mutex::new(None));
13557    let snapshot = editor
13558        .update(cx, |editor, cx| {
13559            let snapshot = editor.buffer().read(cx).snapshot(cx);
13560            let range =
13561                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13562
13563            struct RenderArgs {
13564                row: MultiBufferRow,
13565                folded: bool,
13566                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13567            }
13568
13569            let crease = Crease::inline(
13570                range,
13571                FoldPlaceholder::test(),
13572                {
13573                    let toggle_callback = render_args.clone();
13574                    move |row, folded, callback, _cx| {
13575                        *toggle_callback.lock() = Some(RenderArgs {
13576                            row,
13577                            folded,
13578                            callback,
13579                        });
13580                        div()
13581                    }
13582                },
13583                |_row, _folded, _cx| div(),
13584            );
13585
13586            editor.insert_creases(Some(crease), cx);
13587            let snapshot = editor.snapshot(cx);
13588            let _div =
13589                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13590            snapshot
13591        })
13592        .unwrap();
13593
13594    let render_args = render_args.lock().take().unwrap();
13595    assert_eq!(render_args.row, MultiBufferRow(1));
13596    assert!(!render_args.folded);
13597    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13598
13599    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13600        .unwrap();
13601    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13602    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13603
13604    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13605        .unwrap();
13606    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13607    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13608}
13609
13610#[gpui::test]
13611async fn test_input_text(cx: &mut gpui::TestAppContext) {
13612    init_test(cx, |_| {});
13613    let mut cx = EditorTestContext::new(cx).await;
13614
13615    cx.set_state(
13616        &r#"ˇone
13617        two
13618
13619        three
13620        fourˇ
13621        five
13622
13623        siˇx"#
13624            .unindent(),
13625    );
13626
13627    cx.dispatch_action(HandleInput(String::new()));
13628    cx.assert_editor_state(
13629        &r#"ˇone
13630        two
13631
13632        three
13633        fourˇ
13634        five
13635
13636        siˇx"#
13637            .unindent(),
13638    );
13639
13640    cx.dispatch_action(HandleInput("AAAA".to_string()));
13641    cx.assert_editor_state(
13642        &r#"AAAAˇone
13643        two
13644
13645        three
13646        fourAAAAˇ
13647        five
13648
13649        siAAAAˇx"#
13650            .unindent(),
13651    );
13652}
13653
13654#[gpui::test]
13655async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13656    init_test(cx, |_| {});
13657
13658    let mut cx = EditorTestContext::new(cx).await;
13659    cx.set_state(
13660        r#"let foo = 1;
13661let foo = 2;
13662let foo = 3;
13663let fooˇ = 4;
13664let foo = 5;
13665let foo = 6;
13666let foo = 7;
13667let foo = 8;
13668let foo = 9;
13669let foo = 10;
13670let foo = 11;
13671let foo = 12;
13672let foo = 13;
13673let foo = 14;
13674let foo = 15;"#,
13675    );
13676
13677    cx.update_editor(|e, cx| {
13678        assert_eq!(
13679            e.next_scroll_position,
13680            NextScrollCursorCenterTopBottom::Center,
13681            "Default next scroll direction is center",
13682        );
13683
13684        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13685        assert_eq!(
13686            e.next_scroll_position,
13687            NextScrollCursorCenterTopBottom::Top,
13688            "After center, next scroll direction should be top",
13689        );
13690
13691        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13692        assert_eq!(
13693            e.next_scroll_position,
13694            NextScrollCursorCenterTopBottom::Bottom,
13695            "After top, next scroll direction should be bottom",
13696        );
13697
13698        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13699        assert_eq!(
13700            e.next_scroll_position,
13701            NextScrollCursorCenterTopBottom::Center,
13702            "After bottom, scrolling should start over",
13703        );
13704
13705        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13706        assert_eq!(
13707            e.next_scroll_position,
13708            NextScrollCursorCenterTopBottom::Top,
13709            "Scrolling continues if retriggered fast enough"
13710        );
13711    });
13712
13713    cx.executor()
13714        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13715    cx.executor().run_until_parked();
13716    cx.update_editor(|e, _| {
13717        assert_eq!(
13718            e.next_scroll_position,
13719            NextScrollCursorCenterTopBottom::Center,
13720            "If scrolling is not triggered fast enough, it should reset"
13721        );
13722    });
13723}
13724
13725#[gpui::test]
13726async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13727    init_test(cx, |_| {});
13728    let mut cx = EditorLspTestContext::new_rust(
13729        lsp::ServerCapabilities {
13730            definition_provider: Some(lsp::OneOf::Left(true)),
13731            references_provider: Some(lsp::OneOf::Left(true)),
13732            ..lsp::ServerCapabilities::default()
13733        },
13734        cx,
13735    )
13736    .await;
13737
13738    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13739        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13740            move |params, _| async move {
13741                if empty_go_to_definition {
13742                    Ok(None)
13743                } else {
13744                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13745                        uri: params.text_document_position_params.text_document.uri,
13746                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13747                    })))
13748                }
13749            },
13750        );
13751        let references =
13752            cx.lsp
13753                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13754                    Ok(Some(vec![lsp::Location {
13755                        uri: params.text_document_position.text_document.uri,
13756                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13757                    }]))
13758                });
13759        (go_to_definition, references)
13760    };
13761
13762    cx.set_state(
13763        &r#"fn one() {
13764            let mut a = ˇtwo();
13765        }
13766
13767        fn two() {}"#
13768            .unindent(),
13769    );
13770    set_up_lsp_handlers(false, &mut cx);
13771    let navigated = cx
13772        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13773        .await
13774        .expect("Failed to navigate to definition");
13775    assert_eq!(
13776        navigated,
13777        Navigated::Yes,
13778        "Should have navigated to definition from the GetDefinition response"
13779    );
13780    cx.assert_editor_state(
13781        &r#"fn one() {
13782            let mut a = two();
13783        }
13784
13785        fn «twoˇ»() {}"#
13786            .unindent(),
13787    );
13788
13789    let editors = cx.update_workspace(|workspace, cx| {
13790        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13791    });
13792    cx.update_editor(|_, test_editor_cx| {
13793        assert_eq!(
13794            editors.len(),
13795            1,
13796            "Initially, only one, test, editor should be open in the workspace"
13797        );
13798        assert_eq!(
13799            test_editor_cx.view(),
13800            editors.last().expect("Asserted len is 1")
13801        );
13802    });
13803
13804    set_up_lsp_handlers(true, &mut cx);
13805    let navigated = cx
13806        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13807        .await
13808        .expect("Failed to navigate to lookup references");
13809    assert_eq!(
13810        navigated,
13811        Navigated::Yes,
13812        "Should have navigated to references as a fallback after empty GoToDefinition response"
13813    );
13814    // We should not change the selections in the existing file,
13815    // if opening another milti buffer with the references
13816    cx.assert_editor_state(
13817        &r#"fn one() {
13818            let mut a = two();
13819        }
13820
13821        fn «twoˇ»() {}"#
13822            .unindent(),
13823    );
13824    let editors = cx.update_workspace(|workspace, cx| {
13825        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13826    });
13827    cx.update_editor(|_, test_editor_cx| {
13828        assert_eq!(
13829            editors.len(),
13830            2,
13831            "After falling back to references search, we open a new editor with the results"
13832        );
13833        let references_fallback_text = editors
13834            .into_iter()
13835            .find(|new_editor| new_editor != test_editor_cx.view())
13836            .expect("Should have one non-test editor now")
13837            .read(test_editor_cx)
13838            .text(test_editor_cx);
13839        assert_eq!(
13840            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13841            "Should use the range from the references response and not the GoToDefinition one"
13842        );
13843    });
13844}
13845
13846#[gpui::test]
13847async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13848    init_test(cx, |_| {});
13849
13850    let language = Arc::new(Language::new(
13851        LanguageConfig::default(),
13852        Some(tree_sitter_rust::LANGUAGE.into()),
13853    ));
13854
13855    let text = r#"
13856        #[cfg(test)]
13857        mod tests() {
13858            #[test]
13859            fn runnable_1() {
13860                let a = 1;
13861            }
13862
13863            #[test]
13864            fn runnable_2() {
13865                let a = 1;
13866                let b = 2;
13867            }
13868        }
13869    "#
13870    .unindent();
13871
13872    let fs = FakeFs::new(cx.executor());
13873    fs.insert_file("/file.rs", Default::default()).await;
13874
13875    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13876    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13877    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13878    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13879    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13880
13881    let editor = cx.new_view(|cx| {
13882        Editor::new(
13883            EditorMode::Full,
13884            multi_buffer,
13885            Some(project.clone()),
13886            true,
13887            cx,
13888        )
13889    });
13890
13891    editor.update(cx, |editor, cx| {
13892        editor.tasks.insert(
13893            (buffer.read(cx).remote_id(), 3),
13894            RunnableTasks {
13895                templates: vec![],
13896                offset: MultiBufferOffset(43),
13897                column: 0,
13898                extra_variables: HashMap::default(),
13899                context_range: BufferOffset(43)..BufferOffset(85),
13900            },
13901        );
13902        editor.tasks.insert(
13903            (buffer.read(cx).remote_id(), 8),
13904            RunnableTasks {
13905                templates: vec![],
13906                offset: MultiBufferOffset(86),
13907                column: 0,
13908                extra_variables: HashMap::default(),
13909                context_range: BufferOffset(86)..BufferOffset(191),
13910            },
13911        );
13912
13913        // Test finding task when cursor is inside function body
13914        editor.change_selections(None, cx, |s| {
13915            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13916        });
13917        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13918        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13919
13920        // Test finding task when cursor is on function name
13921        editor.change_selections(None, cx, |s| {
13922            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13923        });
13924        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13925        assert_eq!(row, 8, "Should find task when cursor is on function name");
13926    });
13927}
13928
13929#[gpui::test]
13930async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
13931    init_test(cx, |_| {});
13932
13933    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
13934    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
13935    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
13936
13937    let fs = FakeFs::new(cx.executor());
13938    fs.insert_tree(
13939        "/a",
13940        json!({
13941            "first.rs": sample_text_1,
13942            "second.rs": sample_text_2,
13943            "third.rs": sample_text_3,
13944        }),
13945    )
13946    .await;
13947    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13948    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13949    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13950    let worktree = project.update(cx, |project, cx| {
13951        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
13952        assert_eq!(worktrees.len(), 1);
13953        worktrees.pop().unwrap()
13954    });
13955    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
13956
13957    let buffer_1 = project
13958        .update(cx, |project, cx| {
13959            project.open_buffer((worktree_id, "first.rs"), cx)
13960        })
13961        .await
13962        .unwrap();
13963    let buffer_2 = project
13964        .update(cx, |project, cx| {
13965            project.open_buffer((worktree_id, "second.rs"), cx)
13966        })
13967        .await
13968        .unwrap();
13969    let buffer_3 = project
13970        .update(cx, |project, cx| {
13971            project.open_buffer((worktree_id, "third.rs"), cx)
13972        })
13973        .await
13974        .unwrap();
13975
13976    let multi_buffer = cx.new_model(|cx| {
13977        let mut multi_buffer = MultiBuffer::new(ReadWrite);
13978        multi_buffer.push_excerpts(
13979            buffer_1.clone(),
13980            [
13981                ExcerptRange {
13982                    context: Point::new(0, 0)..Point::new(3, 0),
13983                    primary: None,
13984                },
13985                ExcerptRange {
13986                    context: Point::new(5, 0)..Point::new(7, 0),
13987                    primary: None,
13988                },
13989                ExcerptRange {
13990                    context: Point::new(9, 0)..Point::new(10, 4),
13991                    primary: None,
13992                },
13993            ],
13994            cx,
13995        );
13996        multi_buffer.push_excerpts(
13997            buffer_2.clone(),
13998            [
13999                ExcerptRange {
14000                    context: Point::new(0, 0)..Point::new(3, 0),
14001                    primary: None,
14002                },
14003                ExcerptRange {
14004                    context: Point::new(5, 0)..Point::new(7, 0),
14005                    primary: None,
14006                },
14007                ExcerptRange {
14008                    context: Point::new(9, 0)..Point::new(10, 4),
14009                    primary: None,
14010                },
14011            ],
14012            cx,
14013        );
14014        multi_buffer.push_excerpts(
14015            buffer_3.clone(),
14016            [
14017                ExcerptRange {
14018                    context: Point::new(0, 0)..Point::new(3, 0),
14019                    primary: None,
14020                },
14021                ExcerptRange {
14022                    context: Point::new(5, 0)..Point::new(7, 0),
14023                    primary: None,
14024                },
14025                ExcerptRange {
14026                    context: Point::new(9, 0)..Point::new(10, 4),
14027                    primary: None,
14028                },
14029            ],
14030            cx,
14031        );
14032        multi_buffer
14033    });
14034    let multi_buffer_editor = cx.new_view(|cx| {
14035        Editor::new(
14036            EditorMode::Full,
14037            multi_buffer,
14038            Some(project.clone()),
14039            true,
14040            cx,
14041        )
14042    });
14043
14044    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";
14045    assert_eq!(
14046        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14047        full_text,
14048    );
14049
14050    multi_buffer_editor.update(cx, |editor, cx| {
14051        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14052    });
14053    assert_eq!(
14054        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14055        "\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",
14056        "After folding the first buffer, its text should not be displayed"
14057    );
14058
14059    multi_buffer_editor.update(cx, |editor, cx| {
14060        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14061    });
14062    assert_eq!(
14063        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14064        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14065        "After folding the second buffer, its text should not be displayed"
14066    );
14067
14068    multi_buffer_editor.update(cx, |editor, cx| {
14069        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14070    });
14071    assert_eq!(
14072        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14073        "\n\n\n\n\n",
14074        "After folding the third buffer, its text should not be displayed"
14075    );
14076
14077    // Emulate selection inside the fold logic, that should work
14078    multi_buffer_editor.update(cx, |editor, cx| {
14079        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14080    });
14081
14082    multi_buffer_editor.update(cx, |editor, cx| {
14083        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14084    });
14085    assert_eq!(
14086        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14087        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14088        "After unfolding the second buffer, its text should be displayed"
14089    );
14090
14091    multi_buffer_editor.update(cx, |editor, cx| {
14092        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14093    });
14094    assert_eq!(
14095        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14096        "\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",
14097        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14098    );
14099
14100    multi_buffer_editor.update(cx, |editor, cx| {
14101        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14102    });
14103    assert_eq!(
14104        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14105        full_text,
14106        "After unfolding the all buffers, all original text should be displayed"
14107    );
14108}
14109
14110#[gpui::test]
14111async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14112    init_test(cx, |_| {});
14113
14114    let sample_text_1 = "1111\n2222\n3333".to_string();
14115    let sample_text_2 = "4444\n5555\n6666".to_string();
14116    let sample_text_3 = "7777\n8888\n9999".to_string();
14117
14118    let fs = FakeFs::new(cx.executor());
14119    fs.insert_tree(
14120        "/a",
14121        json!({
14122            "first.rs": sample_text_1,
14123            "second.rs": sample_text_2,
14124            "third.rs": sample_text_3,
14125        }),
14126    )
14127    .await;
14128    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14129    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14130    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14131    let worktree = project.update(cx, |project, cx| {
14132        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14133        assert_eq!(worktrees.len(), 1);
14134        worktrees.pop().unwrap()
14135    });
14136    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14137
14138    let buffer_1 = project
14139        .update(cx, |project, cx| {
14140            project.open_buffer((worktree_id, "first.rs"), cx)
14141        })
14142        .await
14143        .unwrap();
14144    let buffer_2 = project
14145        .update(cx, |project, cx| {
14146            project.open_buffer((worktree_id, "second.rs"), cx)
14147        })
14148        .await
14149        .unwrap();
14150    let buffer_3 = project
14151        .update(cx, |project, cx| {
14152            project.open_buffer((worktree_id, "third.rs"), cx)
14153        })
14154        .await
14155        .unwrap();
14156
14157    let multi_buffer = cx.new_model(|cx| {
14158        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14159        multi_buffer.push_excerpts(
14160            buffer_1.clone(),
14161            [ExcerptRange {
14162                context: Point::new(0, 0)..Point::new(3, 0),
14163                primary: None,
14164            }],
14165            cx,
14166        );
14167        multi_buffer.push_excerpts(
14168            buffer_2.clone(),
14169            [ExcerptRange {
14170                context: Point::new(0, 0)..Point::new(3, 0),
14171                primary: None,
14172            }],
14173            cx,
14174        );
14175        multi_buffer.push_excerpts(
14176            buffer_3.clone(),
14177            [ExcerptRange {
14178                context: Point::new(0, 0)..Point::new(3, 0),
14179                primary: None,
14180            }],
14181            cx,
14182        );
14183        multi_buffer
14184    });
14185
14186    let multi_buffer_editor = cx.new_view(|cx| {
14187        Editor::new(
14188            EditorMode::Full,
14189            multi_buffer,
14190            Some(project.clone()),
14191            true,
14192            cx,
14193        )
14194    });
14195
14196    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14197    assert_eq!(
14198        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14199        full_text,
14200    );
14201
14202    multi_buffer_editor.update(cx, |editor, cx| {
14203        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14204    });
14205    assert_eq!(
14206        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14207        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14208        "After folding the first buffer, its text should not be displayed"
14209    );
14210
14211    multi_buffer_editor.update(cx, |editor, cx| {
14212        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14213    });
14214
14215    assert_eq!(
14216        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14217        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14218        "After folding the second buffer, its text should not be displayed"
14219    );
14220
14221    multi_buffer_editor.update(cx, |editor, cx| {
14222        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14223    });
14224    assert_eq!(
14225        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14226        "\n\n\n\n\n",
14227        "After folding the third buffer, its text should not be displayed"
14228    );
14229
14230    multi_buffer_editor.update(cx, |editor, cx| {
14231        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14232    });
14233    assert_eq!(
14234        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14235        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14236        "After unfolding the second buffer, its text should be displayed"
14237    );
14238
14239    multi_buffer_editor.update(cx, |editor, cx| {
14240        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14241    });
14242    assert_eq!(
14243        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14244        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14245        "After unfolding the first buffer, its text should be displayed"
14246    );
14247
14248    multi_buffer_editor.update(cx, |editor, cx| {
14249        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14250    });
14251    assert_eq!(
14252        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14253        full_text,
14254        "After unfolding all buffers, all original text should be displayed"
14255    );
14256}
14257
14258#[gpui::test]
14259async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14260    init_test(cx, |_| {});
14261
14262    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14263
14264    let fs = FakeFs::new(cx.executor());
14265    fs.insert_tree(
14266        "/a",
14267        json!({
14268            "main.rs": sample_text,
14269        }),
14270    )
14271    .await;
14272    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14273    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14274    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14275    let worktree = project.update(cx, |project, cx| {
14276        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14277        assert_eq!(worktrees.len(), 1);
14278        worktrees.pop().unwrap()
14279    });
14280    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14281
14282    let buffer_1 = project
14283        .update(cx, |project, cx| {
14284            project.open_buffer((worktree_id, "main.rs"), cx)
14285        })
14286        .await
14287        .unwrap();
14288
14289    let multi_buffer = cx.new_model(|cx| {
14290        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14291        multi_buffer.push_excerpts(
14292            buffer_1.clone(),
14293            [ExcerptRange {
14294                context: Point::new(0, 0)
14295                    ..Point::new(
14296                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14297                        0,
14298                    ),
14299                primary: None,
14300            }],
14301            cx,
14302        );
14303        multi_buffer
14304    });
14305    let multi_buffer_editor = cx.new_view(|cx| {
14306        Editor::new(
14307            EditorMode::Full,
14308            multi_buffer,
14309            Some(project.clone()),
14310            true,
14311            cx,
14312        )
14313    });
14314
14315    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14316    multi_buffer_editor.update(cx, |editor, cx| {
14317        enum TestHighlight {}
14318        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14319        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14320        editor.highlight_text::<TestHighlight>(
14321            vec![highlight_range.clone()],
14322            HighlightStyle::color(Hsla::green()),
14323            cx,
14324        );
14325        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14326    });
14327
14328    let full_text = format!("\n\n\n{sample_text}\n");
14329    assert_eq!(
14330        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14331        full_text,
14332    );
14333}
14334
14335#[gpui::test]
14336fn test_inline_completion_text(cx: &mut TestAppContext) {
14337    init_test(cx, |_| {});
14338
14339    // Simple insertion
14340    {
14341        let window = cx.add_window(|cx| {
14342            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14343            Editor::new(EditorMode::Full, buffer, None, true, cx)
14344        });
14345        let cx = &mut VisualTestContext::from_window(*window, cx);
14346
14347        window
14348            .update(cx, |editor, cx| {
14349                let snapshot = editor.snapshot(cx);
14350                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14351                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14352                let edits = vec![(edit_range, " beautiful".to_string())];
14353
14354                let InlineCompletionText::Edit { text, highlights } =
14355                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14356                else {
14357                    panic!("Failed to generate inline completion text");
14358                };
14359
14360                assert_eq!(text, "Hello, beautiful world!");
14361                assert_eq!(highlights.len(), 1);
14362                assert_eq!(highlights[0].0, 6..16);
14363                assert_eq!(
14364                    highlights[0].1.background_color,
14365                    Some(cx.theme().status().created_background)
14366                );
14367            })
14368            .unwrap();
14369    }
14370
14371    // Replacement
14372    {
14373        let window = cx.add_window(|cx| {
14374            let buffer = MultiBuffer::build_simple("This is a test.", cx);
14375            Editor::new(EditorMode::Full, buffer, None, true, cx)
14376        });
14377        let cx = &mut VisualTestContext::from_window(*window, cx);
14378
14379        window
14380            .update(cx, |editor, cx| {
14381                let snapshot = editor.snapshot(cx);
14382                let edits = vec![(
14383                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14384                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
14385                    "That".to_string(),
14386                )];
14387
14388                let InlineCompletionText::Edit { text, highlights } =
14389                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14390                else {
14391                    panic!("Failed to generate inline completion text");
14392                };
14393
14394                assert_eq!(text, "That is a test.");
14395                assert_eq!(highlights.len(), 1);
14396                assert_eq!(highlights[0].0, 0..4);
14397                assert_eq!(
14398                    highlights[0].1.background_color,
14399                    Some(cx.theme().status().created_background)
14400                );
14401            })
14402            .unwrap();
14403    }
14404
14405    // Multiple edits
14406    {
14407        let window = cx.add_window(|cx| {
14408            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14409            Editor::new(EditorMode::Full, buffer, None, true, cx)
14410        });
14411        let cx = &mut VisualTestContext::from_window(*window, cx);
14412
14413        window
14414            .update(cx, |editor, cx| {
14415                let snapshot = editor.snapshot(cx);
14416                let edits = vec![
14417                    (
14418                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14419                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
14420                        "Greetings".into(),
14421                    ),
14422                    (
14423                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
14424                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
14425                        " and universe".into(),
14426                    ),
14427                ];
14428
14429                let InlineCompletionText::Edit { text, highlights } =
14430                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14431                else {
14432                    panic!("Failed to generate inline completion text");
14433                };
14434
14435                assert_eq!(text, "Greetings, world and universe!");
14436                assert_eq!(highlights.len(), 2);
14437                assert_eq!(highlights[0].0, 0..9);
14438                assert_eq!(highlights[1].0, 16..29);
14439                assert_eq!(
14440                    highlights[0].1.background_color,
14441                    Some(cx.theme().status().created_background)
14442                );
14443                assert_eq!(
14444                    highlights[1].1.background_color,
14445                    Some(cx.theme().status().created_background)
14446                );
14447            })
14448            .unwrap();
14449    }
14450
14451    // Multiple lines with edits
14452    {
14453        let window = cx.add_window(|cx| {
14454            let buffer =
14455                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
14456            Editor::new(EditorMode::Full, buffer, None, true, cx)
14457        });
14458        let cx = &mut VisualTestContext::from_window(*window, cx);
14459
14460        window
14461            .update(cx, |editor, cx| {
14462                let snapshot = editor.snapshot(cx);
14463                let edits = vec![
14464                    (
14465                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
14466                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
14467                        "modified".to_string(),
14468                    ),
14469                    (
14470                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
14471                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
14472                        "New third line".to_string(),
14473                    ),
14474                    (
14475                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
14476                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
14477                        " updated".to_string(),
14478                    ),
14479                ];
14480
14481                let InlineCompletionText::Edit { text, highlights } =
14482                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14483                else {
14484                    panic!("Failed to generate inline completion text");
14485                };
14486
14487                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
14488                assert_eq!(highlights.len(), 3);
14489                assert_eq!(highlights[0].0, 7..15); // "modified"
14490                assert_eq!(highlights[1].0, 16..30); // "New third line"
14491                assert_eq!(highlights[2].0, 37..45); // " updated"
14492
14493                for highlight in &highlights {
14494                    assert_eq!(
14495                        highlight.1.background_color,
14496                        Some(cx.theme().status().created_background)
14497                    );
14498                }
14499            })
14500            .unwrap();
14501    }
14502}
14503
14504#[gpui::test]
14505fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
14506    init_test(cx, |_| {});
14507
14508    // Deletion
14509    {
14510        let window = cx.add_window(|cx| {
14511            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14512            Editor::new(EditorMode::Full, buffer, None, true, cx)
14513        });
14514        let cx = &mut VisualTestContext::from_window(*window, cx);
14515
14516        window
14517            .update(cx, |editor, cx| {
14518                let snapshot = editor.snapshot(cx);
14519                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
14520                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
14521                let edits = vec![(edit_range, "".to_string())];
14522
14523                let InlineCompletionText::Edit { text, highlights } =
14524                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14525                else {
14526                    panic!("Failed to generate inline completion text");
14527                };
14528
14529                assert_eq!(text, "Hello, world!");
14530                assert_eq!(highlights.len(), 1);
14531                assert_eq!(highlights[0].0, 5..11);
14532                assert_eq!(
14533                    highlights[0].1.background_color,
14534                    Some(cx.theme().status().deleted_background)
14535                );
14536            })
14537            .unwrap();
14538    }
14539
14540    // Insertion
14541    {
14542        let window = cx.add_window(|cx| {
14543            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14544            Editor::new(EditorMode::Full, buffer, None, true, cx)
14545        });
14546        let cx = &mut VisualTestContext::from_window(*window, cx);
14547
14548        window
14549            .update(cx, |editor, cx| {
14550                let snapshot = editor.snapshot(cx);
14551                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14552                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14553                let edits = vec![(edit_range, " digital".to_string())];
14554
14555                let InlineCompletionText::Edit { text, highlights } =
14556                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14557                else {
14558                    panic!("Failed to generate inline completion text");
14559                };
14560
14561                assert_eq!(text, "Hello, digital world!");
14562                assert_eq!(highlights.len(), 1);
14563                assert_eq!(highlights[0].0, 6..14);
14564                assert_eq!(
14565                    highlights[0].1.background_color,
14566                    Some(cx.theme().status().created_background)
14567                );
14568            })
14569            .unwrap();
14570    }
14571}
14572
14573fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
14574    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
14575    point..point
14576}
14577
14578fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
14579    let (text, ranges) = marked_text_ranges(marked_text, true);
14580    assert_eq!(view.text(cx), text);
14581    assert_eq!(
14582        view.selections.ranges(cx),
14583        ranges,
14584        "Assert selections are {}",
14585        marked_text
14586    );
14587}
14588
14589pub fn handle_signature_help_request(
14590    cx: &mut EditorLspTestContext,
14591    mocked_response: lsp::SignatureHelp,
14592) -> impl Future<Output = ()> {
14593    let mut request =
14594        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
14595            let mocked_response = mocked_response.clone();
14596            async move { Ok(Some(mocked_response)) }
14597        });
14598
14599    async move {
14600        request.next().await;
14601    }
14602}
14603
14604/// Handle completion request passing a marked string specifying where the completion
14605/// should be triggered from using '|' character, what range should be replaced, and what completions
14606/// should be returned using '<' and '>' to delimit the range
14607pub fn handle_completion_request(
14608    cx: &mut EditorLspTestContext,
14609    marked_string: &str,
14610    completions: Vec<&'static str>,
14611    counter: Arc<AtomicUsize>,
14612) -> impl Future<Output = ()> {
14613    let complete_from_marker: TextRangeMarker = '|'.into();
14614    let replace_range_marker: TextRangeMarker = ('<', '>').into();
14615    let (_, mut marked_ranges) = marked_text_ranges_by(
14616        marked_string,
14617        vec![complete_from_marker.clone(), replace_range_marker.clone()],
14618    );
14619
14620    let complete_from_position =
14621        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
14622    let replace_range =
14623        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
14624
14625    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
14626        let completions = completions.clone();
14627        counter.fetch_add(1, atomic::Ordering::Release);
14628        async move {
14629            assert_eq!(params.text_document_position.text_document.uri, url.clone());
14630            assert_eq!(
14631                params.text_document_position.position,
14632                complete_from_position
14633            );
14634            Ok(Some(lsp::CompletionResponse::Array(
14635                completions
14636                    .iter()
14637                    .map(|completion_text| lsp::CompletionItem {
14638                        label: completion_text.to_string(),
14639                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14640                            range: replace_range,
14641                            new_text: completion_text.to_string(),
14642                        })),
14643                        ..Default::default()
14644                    })
14645                    .collect(),
14646            )))
14647        }
14648    });
14649
14650    async move {
14651        request.next().await;
14652    }
14653}
14654
14655fn handle_resolve_completion_request(
14656    cx: &mut EditorLspTestContext,
14657    edits: Option<Vec<(&'static str, &'static str)>>,
14658) -> impl Future<Output = ()> {
14659    let edits = edits.map(|edits| {
14660        edits
14661            .iter()
14662            .map(|(marked_string, new_text)| {
14663                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
14664                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
14665                lsp::TextEdit::new(replace_range, new_text.to_string())
14666            })
14667            .collect::<Vec<_>>()
14668    });
14669
14670    let mut request =
14671        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14672            let edits = edits.clone();
14673            async move {
14674                Ok(lsp::CompletionItem {
14675                    additional_text_edits: edits,
14676                    ..Default::default()
14677                })
14678            }
14679        });
14680
14681    async move {
14682        request.next().await;
14683    }
14684}
14685
14686pub(crate) fn update_test_language_settings(
14687    cx: &mut TestAppContext,
14688    f: impl Fn(&mut AllLanguageSettingsContent),
14689) {
14690    cx.update(|cx| {
14691        SettingsStore::update_global(cx, |store, cx| {
14692            store.update_user_settings::<AllLanguageSettings>(cx, f);
14693        });
14694    });
14695}
14696
14697pub(crate) fn update_test_project_settings(
14698    cx: &mut TestAppContext,
14699    f: impl Fn(&mut ProjectSettings),
14700) {
14701    cx.update(|cx| {
14702        SettingsStore::update_global(cx, |store, cx| {
14703            store.update_user_settings::<ProjectSettings>(cx, f);
14704        });
14705    });
14706}
14707
14708pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14709    cx.update(|cx| {
14710        assets::Assets.load_test_fonts(cx);
14711        let store = SettingsStore::test(cx);
14712        cx.set_global(store);
14713        theme::init(theme::LoadThemes::JustBase, cx);
14714        release_channel::init(SemanticVersion::default(), cx);
14715        client::init_settings(cx);
14716        language::init(cx);
14717        Project::init_settings(cx);
14718        workspace::init_settings(cx);
14719        crate::init(cx);
14720    });
14721
14722    update_test_language_settings(cx, f);
14723}
14724
14725#[track_caller]
14726fn assert_hunk_revert(
14727    not_reverted_text_with_selections: &str,
14728    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14729    expected_reverted_text_with_selections: &str,
14730    base_text: &str,
14731    cx: &mut EditorLspTestContext,
14732) {
14733    cx.set_state(not_reverted_text_with_selections);
14734    cx.set_diff_base(base_text);
14735    cx.executor().run_until_parked();
14736
14737    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14738        let snapshot = editor.snapshot(cx);
14739        let reverted_hunk_statuses = snapshot
14740            .diff_map
14741            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14742            .map(|hunk| hunk_status(&hunk))
14743            .collect::<Vec<_>>();
14744
14745        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14746        reverted_hunk_statuses
14747    });
14748    cx.executor().run_until_parked();
14749    cx.assert_editor_state(expected_reverted_text_with_selections);
14750    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14751}