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.read().is_none()));
 8350    cx.simulate_keystroke("s");
 8351    assert!(cx.editor(|e, _| e.context_menu.read().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.read().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.read().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.read().as_ref() {
 8476            assert_eq!(
 8477                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8478                &["first", "last"]
 8479            );
 8480        } else {
 8481            panic!("expected completion menu to be open");
 8482        }
 8483    });
 8484
 8485    cx.update_editor(|editor, cx| {
 8486        editor.move_page_down(&MovePageDown::default(), cx);
 8487        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8488            assert!(
 8489                menu.selected_item == 1,
 8490                "expected PageDown to select the last item from the context menu"
 8491            );
 8492        } else {
 8493            panic!("expected completion menu to stay open after PageDown");
 8494        }
 8495    });
 8496
 8497    cx.update_editor(|editor, cx| {
 8498        editor.move_page_up(&MovePageUp::default(), cx);
 8499        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 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.read().as_ref() {
 8568            assert_eq!(
 8569                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8570                &["r", "ret", "Range", "return"]
 8571            );
 8572        } else {
 8573            panic!("expected completion menu to be open");
 8574        }
 8575    });
 8576}
 8577
 8578#[gpui::test]
 8579async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8580    init_test(cx, |_| {});
 8581
 8582    let mut cx = EditorLspTestContext::new_rust(
 8583        lsp::ServerCapabilities {
 8584            completion_provider: Some(lsp::CompletionOptions {
 8585                trigger_characters: Some(vec![".".to_string()]),
 8586                resolve_provider: Some(true),
 8587                ..Default::default()
 8588            }),
 8589            ..Default::default()
 8590        },
 8591        cx,
 8592    )
 8593    .await;
 8594
 8595    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8596    cx.simulate_keystroke(".");
 8597    let completion_item = lsp::CompletionItem {
 8598        label: "Some".into(),
 8599        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8600        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8601        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8602            kind: lsp::MarkupKind::Markdown,
 8603            value: "```rust\nSome(2)\n```".to_string(),
 8604        })),
 8605        deprecated: Some(false),
 8606        sort_text: Some("Some".to_string()),
 8607        filter_text: Some("Some".to_string()),
 8608        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8609        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8610            range: lsp::Range {
 8611                start: lsp::Position {
 8612                    line: 0,
 8613                    character: 22,
 8614                },
 8615                end: lsp::Position {
 8616                    line: 0,
 8617                    character: 22,
 8618                },
 8619            },
 8620            new_text: "Some(2)".to_string(),
 8621        })),
 8622        additional_text_edits: Some(vec![lsp::TextEdit {
 8623            range: lsp::Range {
 8624                start: lsp::Position {
 8625                    line: 0,
 8626                    character: 20,
 8627                },
 8628                end: lsp::Position {
 8629                    line: 0,
 8630                    character: 22,
 8631                },
 8632            },
 8633            new_text: "".to_string(),
 8634        }]),
 8635        ..Default::default()
 8636    };
 8637
 8638    let closure_completion_item = completion_item.clone();
 8639    let counter = Arc::new(AtomicUsize::new(0));
 8640    let counter_clone = counter.clone();
 8641    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8642        let task_completion_item = closure_completion_item.clone();
 8643        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8644        async move {
 8645            Ok(Some(lsp::CompletionResponse::Array(vec![
 8646                task_completion_item,
 8647            ])))
 8648        }
 8649    });
 8650
 8651    cx.condition(|editor, _| editor.context_menu_visible())
 8652        .await;
 8653    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8654    assert!(request.next().await.is_some());
 8655    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8656
 8657    cx.simulate_keystroke("S");
 8658    cx.simulate_keystroke("o");
 8659    cx.simulate_keystroke("m");
 8660    cx.condition(|editor, _| editor.context_menu_visible())
 8661        .await;
 8662    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8663    assert!(request.next().await.is_some());
 8664    assert!(request.next().await.is_some());
 8665    assert!(request.next().await.is_some());
 8666    request.close();
 8667    assert!(request.next().await.is_none());
 8668    assert_eq!(
 8669        counter.load(atomic::Ordering::Acquire),
 8670        4,
 8671        "With the completions menu open, only one LSP request should happen per input"
 8672    );
 8673}
 8674
 8675#[gpui::test]
 8676async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8677    init_test(cx, |_| {});
 8678    let mut cx = EditorTestContext::new(cx).await;
 8679    let language = Arc::new(Language::new(
 8680        LanguageConfig {
 8681            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8682            ..Default::default()
 8683        },
 8684        Some(tree_sitter_rust::LANGUAGE.into()),
 8685    ));
 8686    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8687
 8688    // If multiple selections intersect a line, the line is only toggled once.
 8689    cx.set_state(indoc! {"
 8690        fn a() {
 8691            «//b();
 8692            ˇ»// «c();
 8693            //ˇ»  d();
 8694        }
 8695    "});
 8696
 8697    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8698
 8699    cx.assert_editor_state(indoc! {"
 8700        fn a() {
 8701            «b();
 8702            c();
 8703            ˇ» d();
 8704        }
 8705    "});
 8706
 8707    // The comment prefix is inserted at the same column for every line in a
 8708    // selection.
 8709    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8710
 8711    cx.assert_editor_state(indoc! {"
 8712        fn a() {
 8713            // «b();
 8714            // c();
 8715            ˇ»//  d();
 8716        }
 8717    "});
 8718
 8719    // If a selection ends at the beginning of a line, that line is not toggled.
 8720    cx.set_selections_state(indoc! {"
 8721        fn a() {
 8722            // b();
 8723            «// c();
 8724        ˇ»    //  d();
 8725        }
 8726    "});
 8727
 8728    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8729
 8730    cx.assert_editor_state(indoc! {"
 8731        fn a() {
 8732            // b();
 8733            «c();
 8734        ˇ»    //  d();
 8735        }
 8736    "});
 8737
 8738    // If a selection span a single line and is empty, the line is toggled.
 8739    cx.set_state(indoc! {"
 8740        fn a() {
 8741            a();
 8742            b();
 8743        ˇ
 8744        }
 8745    "});
 8746
 8747    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8748
 8749    cx.assert_editor_state(indoc! {"
 8750        fn a() {
 8751            a();
 8752            b();
 8753        //•ˇ
 8754        }
 8755    "});
 8756
 8757    // If a selection span multiple lines, empty lines are not toggled.
 8758    cx.set_state(indoc! {"
 8759        fn a() {
 8760            «a();
 8761
 8762            c();ˇ»
 8763        }
 8764    "});
 8765
 8766    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8767
 8768    cx.assert_editor_state(indoc! {"
 8769        fn a() {
 8770            // «a();
 8771
 8772            // c();ˇ»
 8773        }
 8774    "});
 8775
 8776    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8777    cx.set_state(indoc! {"
 8778        fn a() {
 8779            «// a();
 8780            /// b();
 8781            //! c();ˇ»
 8782        }
 8783    "});
 8784
 8785    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8786
 8787    cx.assert_editor_state(indoc! {"
 8788        fn a() {
 8789            «a();
 8790            b();
 8791            c();ˇ»
 8792        }
 8793    "});
 8794}
 8795
 8796#[gpui::test]
 8797async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8798    init_test(cx, |_| {});
 8799    let mut cx = EditorTestContext::new(cx).await;
 8800    let language = Arc::new(Language::new(
 8801        LanguageConfig {
 8802            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8803            ..Default::default()
 8804        },
 8805        Some(tree_sitter_rust::LANGUAGE.into()),
 8806    ));
 8807    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8808
 8809    let toggle_comments = &ToggleComments {
 8810        advance_downwards: false,
 8811        ignore_indent: true,
 8812    };
 8813
 8814    // If multiple selections intersect a line, the line is only toggled once.
 8815    cx.set_state(indoc! {"
 8816        fn a() {
 8817        //    «b();
 8818        //    c();
 8819        //    ˇ» d();
 8820        }
 8821    "});
 8822
 8823    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8824
 8825    cx.assert_editor_state(indoc! {"
 8826        fn a() {
 8827            «b();
 8828            c();
 8829            ˇ» d();
 8830        }
 8831    "});
 8832
 8833    // The comment prefix is inserted at the beginning of each line
 8834    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8835
 8836    cx.assert_editor_state(indoc! {"
 8837        fn a() {
 8838        //    «b();
 8839        //    c();
 8840        //    ˇ» d();
 8841        }
 8842    "});
 8843
 8844    // If a selection ends at the beginning of a line, that line is not toggled.
 8845    cx.set_selections_state(indoc! {"
 8846        fn a() {
 8847        //    b();
 8848        //    «c();
 8849        ˇ»//     d();
 8850        }
 8851    "});
 8852
 8853    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8854
 8855    cx.assert_editor_state(indoc! {"
 8856        fn a() {
 8857        //    b();
 8858            «c();
 8859        ˇ»//     d();
 8860        }
 8861    "});
 8862
 8863    // If a selection span a single line and is empty, the line is toggled.
 8864    cx.set_state(indoc! {"
 8865        fn a() {
 8866            a();
 8867            b();
 8868        ˇ
 8869        }
 8870    "});
 8871
 8872    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8873
 8874    cx.assert_editor_state(indoc! {"
 8875        fn a() {
 8876            a();
 8877            b();
 8878        //ˇ
 8879        }
 8880    "});
 8881
 8882    // If a selection span multiple lines, empty lines are not toggled.
 8883    cx.set_state(indoc! {"
 8884        fn a() {
 8885            «a();
 8886
 8887            c();ˇ»
 8888        }
 8889    "});
 8890
 8891    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8892
 8893    cx.assert_editor_state(indoc! {"
 8894        fn a() {
 8895        //    «a();
 8896
 8897        //    c();ˇ»
 8898        }
 8899    "});
 8900
 8901    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8902    cx.set_state(indoc! {"
 8903        fn a() {
 8904        //    «a();
 8905        ///    b();
 8906        //!    c();ˇ»
 8907        }
 8908    "});
 8909
 8910    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8911
 8912    cx.assert_editor_state(indoc! {"
 8913        fn a() {
 8914            «a();
 8915            b();
 8916            c();ˇ»
 8917        }
 8918    "});
 8919}
 8920
 8921#[gpui::test]
 8922async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8923    init_test(cx, |_| {});
 8924
 8925    let language = Arc::new(Language::new(
 8926        LanguageConfig {
 8927            line_comments: vec!["// ".into()],
 8928            ..Default::default()
 8929        },
 8930        Some(tree_sitter_rust::LANGUAGE.into()),
 8931    ));
 8932
 8933    let mut cx = EditorTestContext::new(cx).await;
 8934
 8935    cx.language_registry().add(language.clone());
 8936    cx.update_buffer(|buffer, cx| {
 8937        buffer.set_language(Some(language), cx);
 8938    });
 8939
 8940    let toggle_comments = &ToggleComments {
 8941        advance_downwards: true,
 8942        ignore_indent: false,
 8943    };
 8944
 8945    // Single cursor on one line -> advance
 8946    // Cursor moves horizontally 3 characters as well on non-blank line
 8947    cx.set_state(indoc!(
 8948        "fn a() {
 8949             ˇdog();
 8950             cat();
 8951        }"
 8952    ));
 8953    cx.update_editor(|editor, cx| {
 8954        editor.toggle_comments(toggle_comments, cx);
 8955    });
 8956    cx.assert_editor_state(indoc!(
 8957        "fn a() {
 8958             // dog();
 8959             catˇ();
 8960        }"
 8961    ));
 8962
 8963    // Single selection on one line -> don't advance
 8964    cx.set_state(indoc!(
 8965        "fn a() {
 8966             «dog()ˇ»;
 8967             cat();
 8968        }"
 8969    ));
 8970    cx.update_editor(|editor, cx| {
 8971        editor.toggle_comments(toggle_comments, cx);
 8972    });
 8973    cx.assert_editor_state(indoc!(
 8974        "fn a() {
 8975             // «dog()ˇ»;
 8976             cat();
 8977        }"
 8978    ));
 8979
 8980    // Multiple cursors on one line -> advance
 8981    cx.set_state(indoc!(
 8982        "fn a() {
 8983             ˇdˇog();
 8984             cat();
 8985        }"
 8986    ));
 8987    cx.update_editor(|editor, cx| {
 8988        editor.toggle_comments(toggle_comments, cx);
 8989    });
 8990    cx.assert_editor_state(indoc!(
 8991        "fn a() {
 8992             // dog();
 8993             catˇ(ˇ);
 8994        }"
 8995    ));
 8996
 8997    // Multiple cursors on one line, with selection -> don't advance
 8998    cx.set_state(indoc!(
 8999        "fn a() {
 9000             ˇdˇog«()ˇ»;
 9001             cat();
 9002        }"
 9003    ));
 9004    cx.update_editor(|editor, cx| {
 9005        editor.toggle_comments(toggle_comments, cx);
 9006    });
 9007    cx.assert_editor_state(indoc!(
 9008        "fn a() {
 9009             // ˇdˇog«()ˇ»;
 9010             cat();
 9011        }"
 9012    ));
 9013
 9014    // Single cursor on one line -> advance
 9015    // Cursor moves to column 0 on blank line
 9016    cx.set_state(indoc!(
 9017        "fn a() {
 9018             ˇdog();
 9019
 9020             cat();
 9021        }"
 9022    ));
 9023    cx.update_editor(|editor, cx| {
 9024        editor.toggle_comments(toggle_comments, cx);
 9025    });
 9026    cx.assert_editor_state(indoc!(
 9027        "fn a() {
 9028             // dog();
 9029        ˇ
 9030             cat();
 9031        }"
 9032    ));
 9033
 9034    // Single cursor on one line -> advance
 9035    // Cursor starts and ends at column 0
 9036    cx.set_state(indoc!(
 9037        "fn a() {
 9038         ˇ    dog();
 9039             cat();
 9040        }"
 9041    ));
 9042    cx.update_editor(|editor, cx| {
 9043        editor.toggle_comments(toggle_comments, cx);
 9044    });
 9045    cx.assert_editor_state(indoc!(
 9046        "fn a() {
 9047             // dog();
 9048         ˇ    cat();
 9049        }"
 9050    ));
 9051}
 9052
 9053#[gpui::test]
 9054async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9055    init_test(cx, |_| {});
 9056
 9057    let mut cx = EditorTestContext::new(cx).await;
 9058
 9059    let html_language = Arc::new(
 9060        Language::new(
 9061            LanguageConfig {
 9062                name: "HTML".into(),
 9063                block_comment: Some(("<!-- ".into(), " -->".into())),
 9064                ..Default::default()
 9065            },
 9066            Some(tree_sitter_html::language()),
 9067        )
 9068        .with_injection_query(
 9069            r#"
 9070            (script_element
 9071                (raw_text) @content
 9072                (#set! "language" "javascript"))
 9073            "#,
 9074        )
 9075        .unwrap(),
 9076    );
 9077
 9078    let javascript_language = Arc::new(Language::new(
 9079        LanguageConfig {
 9080            name: "JavaScript".into(),
 9081            line_comments: vec!["// ".into()],
 9082            ..Default::default()
 9083        },
 9084        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9085    ));
 9086
 9087    cx.language_registry().add(html_language.clone());
 9088    cx.language_registry().add(javascript_language.clone());
 9089    cx.update_buffer(|buffer, cx| {
 9090        buffer.set_language(Some(html_language), cx);
 9091    });
 9092
 9093    // Toggle comments for empty selections
 9094    cx.set_state(
 9095        &r#"
 9096            <p>A</p>ˇ
 9097            <p>B</p>ˇ
 9098            <p>C</p>ˇ
 9099        "#
 9100        .unindent(),
 9101    );
 9102    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9103    cx.assert_editor_state(
 9104        &r#"
 9105            <!-- <p>A</p>ˇ -->
 9106            <!-- <p>B</p>ˇ -->
 9107            <!-- <p>C</p>ˇ -->
 9108        "#
 9109        .unindent(),
 9110    );
 9111    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9112    cx.assert_editor_state(
 9113        &r#"
 9114            <p>A</p>ˇ
 9115            <p>B</p>ˇ
 9116            <p>C</p>ˇ
 9117        "#
 9118        .unindent(),
 9119    );
 9120
 9121    // Toggle comments for mixture of empty and non-empty selections, where
 9122    // multiple selections occupy a given line.
 9123    cx.set_state(
 9124        &r#"
 9125            <p>A«</p>
 9126            <p>ˇ»B</p>ˇ
 9127            <p>C«</p>
 9128            <p>ˇ»D</p>ˇ
 9129        "#
 9130        .unindent(),
 9131    );
 9132
 9133    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9134    cx.assert_editor_state(
 9135        &r#"
 9136            <!-- <p>A«</p>
 9137            <p>ˇ»B</p>ˇ -->
 9138            <!-- <p>C«</p>
 9139            <p>ˇ»D</p>ˇ -->
 9140        "#
 9141        .unindent(),
 9142    );
 9143    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9144    cx.assert_editor_state(
 9145        &r#"
 9146            <p>A«</p>
 9147            <p>ˇ»B</p>ˇ
 9148            <p>C«</p>
 9149            <p>ˇ»D</p>ˇ
 9150        "#
 9151        .unindent(),
 9152    );
 9153
 9154    // Toggle comments when different languages are active for different
 9155    // selections.
 9156    cx.set_state(
 9157        &r#"
 9158            ˇ<script>
 9159                ˇvar x = new Y();
 9160            ˇ</script>
 9161        "#
 9162        .unindent(),
 9163    );
 9164    cx.executor().run_until_parked();
 9165    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9166    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9167    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9168    cx.assert_editor_state(
 9169        &r#"
 9170            <!-- ˇ<script> -->
 9171                // ˇvar x = new Y();
 9172            // ˇ</script>
 9173        "#
 9174        .unindent(),
 9175    );
 9176}
 9177
 9178#[gpui::test]
 9179fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9180    init_test(cx, |_| {});
 9181
 9182    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9183    let multibuffer = cx.new_model(|cx| {
 9184        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9185        multibuffer.push_excerpts(
 9186            buffer.clone(),
 9187            [
 9188                ExcerptRange {
 9189                    context: Point::new(0, 0)..Point::new(0, 4),
 9190                    primary: None,
 9191                },
 9192                ExcerptRange {
 9193                    context: Point::new(1, 0)..Point::new(1, 4),
 9194                    primary: None,
 9195                },
 9196            ],
 9197            cx,
 9198        );
 9199        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9200        multibuffer
 9201    });
 9202
 9203    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9204    view.update(cx, |view, cx| {
 9205        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9206        view.change_selections(None, cx, |s| {
 9207            s.select_ranges([
 9208                Point::new(0, 0)..Point::new(0, 0),
 9209                Point::new(1, 0)..Point::new(1, 0),
 9210            ])
 9211        });
 9212
 9213        view.handle_input("X", cx);
 9214        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9215        assert_eq!(
 9216            view.selections.ranges(cx),
 9217            [
 9218                Point::new(0, 1)..Point::new(0, 1),
 9219                Point::new(1, 1)..Point::new(1, 1),
 9220            ]
 9221        );
 9222
 9223        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9224        view.change_selections(None, cx, |s| {
 9225            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9226        });
 9227        view.backspace(&Default::default(), cx);
 9228        assert_eq!(view.text(cx), "Xa\nbbb");
 9229        assert_eq!(
 9230            view.selections.ranges(cx),
 9231            [Point::new(1, 0)..Point::new(1, 0)]
 9232        );
 9233
 9234        view.change_selections(None, cx, |s| {
 9235            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9236        });
 9237        view.backspace(&Default::default(), cx);
 9238        assert_eq!(view.text(cx), "X\nbb");
 9239        assert_eq!(
 9240            view.selections.ranges(cx),
 9241            [Point::new(0, 1)..Point::new(0, 1)]
 9242        );
 9243    });
 9244}
 9245
 9246#[gpui::test]
 9247fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9248    init_test(cx, |_| {});
 9249
 9250    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9251    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9252        indoc! {"
 9253            [aaaa
 9254            (bbbb]
 9255            cccc)",
 9256        },
 9257        markers.clone(),
 9258    );
 9259    let excerpt_ranges = markers.into_iter().map(|marker| {
 9260        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9261        ExcerptRange {
 9262            context,
 9263            primary: None,
 9264        }
 9265    });
 9266    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9267    let multibuffer = cx.new_model(|cx| {
 9268        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9269        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9270        multibuffer
 9271    });
 9272
 9273    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9274    view.update(cx, |view, cx| {
 9275        let (expected_text, selection_ranges) = marked_text_ranges(
 9276            indoc! {"
 9277                aaaa
 9278                bˇbbb
 9279                bˇbbˇb
 9280                cccc"
 9281            },
 9282            true,
 9283        );
 9284        assert_eq!(view.text(cx), expected_text);
 9285        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9286
 9287        view.handle_input("X", cx);
 9288
 9289        let (expected_text, expected_selections) = marked_text_ranges(
 9290            indoc! {"
 9291                aaaa
 9292                bXˇbbXb
 9293                bXˇbbXˇb
 9294                cccc"
 9295            },
 9296            false,
 9297        );
 9298        assert_eq!(view.text(cx), expected_text);
 9299        assert_eq!(view.selections.ranges(cx), expected_selections);
 9300
 9301        view.newline(&Newline, cx);
 9302        let (expected_text, expected_selections) = marked_text_ranges(
 9303            indoc! {"
 9304                aaaa
 9305                bX
 9306                ˇbbX
 9307                b
 9308                bX
 9309                ˇbbX
 9310                ˇb
 9311                cccc"
 9312            },
 9313            false,
 9314        );
 9315        assert_eq!(view.text(cx), expected_text);
 9316        assert_eq!(view.selections.ranges(cx), expected_selections);
 9317    });
 9318}
 9319
 9320#[gpui::test]
 9321fn test_refresh_selections(cx: &mut TestAppContext) {
 9322    init_test(cx, |_| {});
 9323
 9324    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9325    let mut excerpt1_id = None;
 9326    let multibuffer = cx.new_model(|cx| {
 9327        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9328        excerpt1_id = multibuffer
 9329            .push_excerpts(
 9330                buffer.clone(),
 9331                [
 9332                    ExcerptRange {
 9333                        context: Point::new(0, 0)..Point::new(1, 4),
 9334                        primary: None,
 9335                    },
 9336                    ExcerptRange {
 9337                        context: Point::new(1, 0)..Point::new(2, 4),
 9338                        primary: None,
 9339                    },
 9340                ],
 9341                cx,
 9342            )
 9343            .into_iter()
 9344            .next();
 9345        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9346        multibuffer
 9347    });
 9348
 9349    let editor = cx.add_window(|cx| {
 9350        let mut editor = build_editor(multibuffer.clone(), cx);
 9351        let snapshot = editor.snapshot(cx);
 9352        editor.change_selections(None, cx, |s| {
 9353            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9354        });
 9355        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9356        assert_eq!(
 9357            editor.selections.ranges(cx),
 9358            [
 9359                Point::new(1, 3)..Point::new(1, 3),
 9360                Point::new(2, 1)..Point::new(2, 1),
 9361            ]
 9362        );
 9363        editor
 9364    });
 9365
 9366    // Refreshing selections is a no-op when excerpts haven't changed.
 9367    _ = editor.update(cx, |editor, cx| {
 9368        editor.change_selections(None, cx, |s| s.refresh());
 9369        assert_eq!(
 9370            editor.selections.ranges(cx),
 9371            [
 9372                Point::new(1, 3)..Point::new(1, 3),
 9373                Point::new(2, 1)..Point::new(2, 1),
 9374            ]
 9375        );
 9376    });
 9377
 9378    multibuffer.update(cx, |multibuffer, cx| {
 9379        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9380    });
 9381    _ = editor.update(cx, |editor, cx| {
 9382        // Removing an excerpt causes the first selection to become degenerate.
 9383        assert_eq!(
 9384            editor.selections.ranges(cx),
 9385            [
 9386                Point::new(0, 0)..Point::new(0, 0),
 9387                Point::new(0, 1)..Point::new(0, 1)
 9388            ]
 9389        );
 9390
 9391        // Refreshing selections will relocate the first selection to the original buffer
 9392        // location.
 9393        editor.change_selections(None, cx, |s| s.refresh());
 9394        assert_eq!(
 9395            editor.selections.ranges(cx),
 9396            [
 9397                Point::new(0, 1)..Point::new(0, 1),
 9398                Point::new(0, 3)..Point::new(0, 3)
 9399            ]
 9400        );
 9401        assert!(editor.selections.pending_anchor().is_some());
 9402    });
 9403}
 9404
 9405#[gpui::test]
 9406fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9407    init_test(cx, |_| {});
 9408
 9409    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9410    let mut excerpt1_id = None;
 9411    let multibuffer = cx.new_model(|cx| {
 9412        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9413        excerpt1_id = multibuffer
 9414            .push_excerpts(
 9415                buffer.clone(),
 9416                [
 9417                    ExcerptRange {
 9418                        context: Point::new(0, 0)..Point::new(1, 4),
 9419                        primary: None,
 9420                    },
 9421                    ExcerptRange {
 9422                        context: Point::new(1, 0)..Point::new(2, 4),
 9423                        primary: None,
 9424                    },
 9425                ],
 9426                cx,
 9427            )
 9428            .into_iter()
 9429            .next();
 9430        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9431        multibuffer
 9432    });
 9433
 9434    let editor = cx.add_window(|cx| {
 9435        let mut editor = build_editor(multibuffer.clone(), cx);
 9436        let snapshot = editor.snapshot(cx);
 9437        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9438        assert_eq!(
 9439            editor.selections.ranges(cx),
 9440            [Point::new(1, 3)..Point::new(1, 3)]
 9441        );
 9442        editor
 9443    });
 9444
 9445    multibuffer.update(cx, |multibuffer, cx| {
 9446        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9447    });
 9448    _ = editor.update(cx, |editor, cx| {
 9449        assert_eq!(
 9450            editor.selections.ranges(cx),
 9451            [Point::new(0, 0)..Point::new(0, 0)]
 9452        );
 9453
 9454        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9455        editor.change_selections(None, cx, |s| s.refresh());
 9456        assert_eq!(
 9457            editor.selections.ranges(cx),
 9458            [Point::new(0, 3)..Point::new(0, 3)]
 9459        );
 9460        assert!(editor.selections.pending_anchor().is_some());
 9461    });
 9462}
 9463
 9464#[gpui::test]
 9465async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9466    init_test(cx, |_| {});
 9467
 9468    let language = Arc::new(
 9469        Language::new(
 9470            LanguageConfig {
 9471                brackets: BracketPairConfig {
 9472                    pairs: vec![
 9473                        BracketPair {
 9474                            start: "{".to_string(),
 9475                            end: "}".to_string(),
 9476                            close: true,
 9477                            surround: true,
 9478                            newline: true,
 9479                        },
 9480                        BracketPair {
 9481                            start: "/* ".to_string(),
 9482                            end: " */".to_string(),
 9483                            close: true,
 9484                            surround: true,
 9485                            newline: true,
 9486                        },
 9487                    ],
 9488                    ..Default::default()
 9489                },
 9490                ..Default::default()
 9491            },
 9492            Some(tree_sitter_rust::LANGUAGE.into()),
 9493        )
 9494        .with_indents_query("")
 9495        .unwrap(),
 9496    );
 9497
 9498    let text = concat!(
 9499        "{   }\n",     //
 9500        "  x\n",       //
 9501        "  /*   */\n", //
 9502        "x\n",         //
 9503        "{{} }\n",     //
 9504    );
 9505
 9506    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9507    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9508    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9509    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9510        .await;
 9511
 9512    view.update(cx, |view, cx| {
 9513        view.change_selections(None, cx, |s| {
 9514            s.select_display_ranges([
 9515                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9516                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9517                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9518            ])
 9519        });
 9520        view.newline(&Newline, cx);
 9521
 9522        assert_eq!(
 9523            view.buffer().read(cx).read(cx).text(),
 9524            concat!(
 9525                "{ \n",    // Suppress rustfmt
 9526                "\n",      //
 9527                "}\n",     //
 9528                "  x\n",   //
 9529                "  /* \n", //
 9530                "  \n",    //
 9531                "  */\n",  //
 9532                "x\n",     //
 9533                "{{} \n",  //
 9534                "}\n",     //
 9535            )
 9536        );
 9537    });
 9538}
 9539
 9540#[gpui::test]
 9541fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9542    init_test(cx, |_| {});
 9543
 9544    let editor = cx.add_window(|cx| {
 9545        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9546        build_editor(buffer.clone(), cx)
 9547    });
 9548
 9549    _ = editor.update(cx, |editor, cx| {
 9550        struct Type1;
 9551        struct Type2;
 9552
 9553        let buffer = editor.buffer.read(cx).snapshot(cx);
 9554
 9555        let anchor_range =
 9556            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9557
 9558        editor.highlight_background::<Type1>(
 9559            &[
 9560                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9561                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9562                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9563                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9564            ],
 9565            |_| Hsla::red(),
 9566            cx,
 9567        );
 9568        editor.highlight_background::<Type2>(
 9569            &[
 9570                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9571                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9572                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9573                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9574            ],
 9575            |_| Hsla::green(),
 9576            cx,
 9577        );
 9578
 9579        let snapshot = editor.snapshot(cx);
 9580        let mut highlighted_ranges = editor.background_highlights_in_range(
 9581            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9582            &snapshot,
 9583            cx.theme().colors(),
 9584        );
 9585        // Enforce a consistent ordering based on color without relying on the ordering of the
 9586        // highlight's `TypeId` which is non-executor.
 9587        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9588        assert_eq!(
 9589            highlighted_ranges,
 9590            &[
 9591                (
 9592                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9593                    Hsla::red(),
 9594                ),
 9595                (
 9596                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9597                    Hsla::red(),
 9598                ),
 9599                (
 9600                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9601                    Hsla::green(),
 9602                ),
 9603                (
 9604                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9605                    Hsla::green(),
 9606                ),
 9607            ]
 9608        );
 9609        assert_eq!(
 9610            editor.background_highlights_in_range(
 9611                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9612                &snapshot,
 9613                cx.theme().colors(),
 9614            ),
 9615            &[(
 9616                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9617                Hsla::red(),
 9618            )]
 9619        );
 9620    });
 9621}
 9622
 9623#[gpui::test]
 9624async fn test_following(cx: &mut gpui::TestAppContext) {
 9625    init_test(cx, |_| {});
 9626
 9627    let fs = FakeFs::new(cx.executor());
 9628    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9629
 9630    let buffer = project.update(cx, |project, cx| {
 9631        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9632        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9633    });
 9634    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9635    let follower = cx.update(|cx| {
 9636        cx.open_window(
 9637            WindowOptions {
 9638                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9639                    gpui::Point::new(px(0.), px(0.)),
 9640                    gpui::Point::new(px(10.), px(80.)),
 9641                ))),
 9642                ..Default::default()
 9643            },
 9644            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9645        )
 9646        .unwrap()
 9647    });
 9648
 9649    let is_still_following = Rc::new(RefCell::new(true));
 9650    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9651    let pending_update = Rc::new(RefCell::new(None));
 9652    _ = follower.update(cx, {
 9653        let update = pending_update.clone();
 9654        let is_still_following = is_still_following.clone();
 9655        let follower_edit_event_count = follower_edit_event_count.clone();
 9656        |_, cx| {
 9657            cx.subscribe(
 9658                &leader.root_view(cx).unwrap(),
 9659                move |_, leader, event, cx| {
 9660                    leader
 9661                        .read(cx)
 9662                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9663                },
 9664            )
 9665            .detach();
 9666
 9667            cx.subscribe(
 9668                &follower.root_view(cx).unwrap(),
 9669                move |_, _, event: &EditorEvent, _cx| {
 9670                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9671                        *is_still_following.borrow_mut() = false;
 9672                    }
 9673
 9674                    if let EditorEvent::BufferEdited = event {
 9675                        *follower_edit_event_count.borrow_mut() += 1;
 9676                    }
 9677                },
 9678            )
 9679            .detach();
 9680        }
 9681    });
 9682
 9683    // Update the selections only
 9684    _ = leader.update(cx, |leader, cx| {
 9685        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9686    });
 9687    follower
 9688        .update(cx, |follower, cx| {
 9689            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9690        })
 9691        .unwrap()
 9692        .await
 9693        .unwrap();
 9694    _ = follower.update(cx, |follower, cx| {
 9695        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9696    });
 9697    assert!(*is_still_following.borrow());
 9698    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9699
 9700    // Update the scroll position only
 9701    _ = leader.update(cx, |leader, cx| {
 9702        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9703    });
 9704    follower
 9705        .update(cx, |follower, cx| {
 9706            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9707        })
 9708        .unwrap()
 9709        .await
 9710        .unwrap();
 9711    assert_eq!(
 9712        follower
 9713            .update(cx, |follower, cx| follower.scroll_position(cx))
 9714            .unwrap(),
 9715        gpui::Point::new(1.5, 3.5)
 9716    );
 9717    assert!(*is_still_following.borrow());
 9718    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9719
 9720    // Update the selections and scroll position. The follower's scroll position is updated
 9721    // via autoscroll, not via the leader's exact scroll position.
 9722    _ = leader.update(cx, |leader, cx| {
 9723        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9724        leader.request_autoscroll(Autoscroll::newest(), cx);
 9725        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9726    });
 9727    follower
 9728        .update(cx, |follower, cx| {
 9729            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9730        })
 9731        .unwrap()
 9732        .await
 9733        .unwrap();
 9734    _ = follower.update(cx, |follower, cx| {
 9735        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9736        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9737    });
 9738    assert!(*is_still_following.borrow());
 9739
 9740    // Creating a pending selection that precedes another selection
 9741    _ = leader.update(cx, |leader, cx| {
 9742        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9743        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9744    });
 9745    follower
 9746        .update(cx, |follower, cx| {
 9747            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9748        })
 9749        .unwrap()
 9750        .await
 9751        .unwrap();
 9752    _ = follower.update(cx, |follower, cx| {
 9753        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9754    });
 9755    assert!(*is_still_following.borrow());
 9756
 9757    // Extend the pending selection so that it surrounds another selection
 9758    _ = leader.update(cx, |leader, cx| {
 9759        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9760    });
 9761    follower
 9762        .update(cx, |follower, cx| {
 9763            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9764        })
 9765        .unwrap()
 9766        .await
 9767        .unwrap();
 9768    _ = follower.update(cx, |follower, cx| {
 9769        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9770    });
 9771
 9772    // Scrolling locally breaks the follow
 9773    _ = follower.update(cx, |follower, cx| {
 9774        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9775        follower.set_scroll_anchor(
 9776            ScrollAnchor {
 9777                anchor: top_anchor,
 9778                offset: gpui::Point::new(0.0, 0.5),
 9779            },
 9780            cx,
 9781        );
 9782    });
 9783    assert!(!(*is_still_following.borrow()));
 9784}
 9785
 9786#[gpui::test]
 9787async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9788    init_test(cx, |_| {});
 9789
 9790    let fs = FakeFs::new(cx.executor());
 9791    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9792    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9793    let pane = workspace
 9794        .update(cx, |workspace, _| workspace.active_pane().clone())
 9795        .unwrap();
 9796
 9797    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9798
 9799    let leader = pane.update(cx, |_, cx| {
 9800        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9801        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9802    });
 9803
 9804    // Start following the editor when it has no excerpts.
 9805    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9806    let follower_1 = cx
 9807        .update_window(*workspace.deref(), |_, cx| {
 9808            Editor::from_state_proto(
 9809                workspace.root_view(cx).unwrap(),
 9810                ViewId {
 9811                    creator: Default::default(),
 9812                    id: 0,
 9813                },
 9814                &mut state_message,
 9815                cx,
 9816            )
 9817        })
 9818        .unwrap()
 9819        .unwrap()
 9820        .await
 9821        .unwrap();
 9822
 9823    let update_message = Rc::new(RefCell::new(None));
 9824    follower_1.update(cx, {
 9825        let update = update_message.clone();
 9826        |_, cx| {
 9827            cx.subscribe(&leader, move |_, leader, event, cx| {
 9828                leader
 9829                    .read(cx)
 9830                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9831            })
 9832            .detach();
 9833        }
 9834    });
 9835
 9836    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9837        (
 9838            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9839            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9840        )
 9841    });
 9842
 9843    // Insert some excerpts.
 9844    leader.update(cx, |leader, cx| {
 9845        leader.buffer.update(cx, |multibuffer, cx| {
 9846            let excerpt_ids = multibuffer.push_excerpts(
 9847                buffer_1.clone(),
 9848                [
 9849                    ExcerptRange {
 9850                        context: 1..6,
 9851                        primary: None,
 9852                    },
 9853                    ExcerptRange {
 9854                        context: 12..15,
 9855                        primary: None,
 9856                    },
 9857                    ExcerptRange {
 9858                        context: 0..3,
 9859                        primary: None,
 9860                    },
 9861                ],
 9862                cx,
 9863            );
 9864            multibuffer.insert_excerpts_after(
 9865                excerpt_ids[0],
 9866                buffer_2.clone(),
 9867                [
 9868                    ExcerptRange {
 9869                        context: 8..12,
 9870                        primary: None,
 9871                    },
 9872                    ExcerptRange {
 9873                        context: 0..6,
 9874                        primary: None,
 9875                    },
 9876                ],
 9877                cx,
 9878            );
 9879        });
 9880    });
 9881
 9882    // Apply the update of adding the excerpts.
 9883    follower_1
 9884        .update(cx, |follower, cx| {
 9885            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9886        })
 9887        .await
 9888        .unwrap();
 9889    assert_eq!(
 9890        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9891        leader.update(cx, |editor, cx| editor.text(cx))
 9892    );
 9893    update_message.borrow_mut().take();
 9894
 9895    // Start following separately after it already has excerpts.
 9896    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9897    let follower_2 = cx
 9898        .update_window(*workspace.deref(), |_, cx| {
 9899            Editor::from_state_proto(
 9900                workspace.root_view(cx).unwrap().clone(),
 9901                ViewId {
 9902                    creator: Default::default(),
 9903                    id: 0,
 9904                },
 9905                &mut state_message,
 9906                cx,
 9907            )
 9908        })
 9909        .unwrap()
 9910        .unwrap()
 9911        .await
 9912        .unwrap();
 9913    assert_eq!(
 9914        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9915        leader.update(cx, |editor, cx| editor.text(cx))
 9916    );
 9917
 9918    // Remove some excerpts.
 9919    leader.update(cx, |leader, cx| {
 9920        leader.buffer.update(cx, |multibuffer, cx| {
 9921            let excerpt_ids = multibuffer.excerpt_ids();
 9922            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9923            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9924        });
 9925    });
 9926
 9927    // Apply the update of removing the excerpts.
 9928    follower_1
 9929        .update(cx, |follower, cx| {
 9930            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9931        })
 9932        .await
 9933        .unwrap();
 9934    follower_2
 9935        .update(cx, |follower, cx| {
 9936            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9937        })
 9938        .await
 9939        .unwrap();
 9940    update_message.borrow_mut().take();
 9941    assert_eq!(
 9942        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9943        leader.update(cx, |editor, cx| editor.text(cx))
 9944    );
 9945}
 9946
 9947#[gpui::test]
 9948async fn go_to_prev_overlapping_diagnostic(
 9949    executor: BackgroundExecutor,
 9950    cx: &mut gpui::TestAppContext,
 9951) {
 9952    init_test(cx, |_| {});
 9953
 9954    let mut cx = EditorTestContext::new(cx).await;
 9955    let lsp_store =
 9956        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
 9957
 9958    cx.set_state(indoc! {"
 9959        ˇfn func(abc def: i32) -> u32 {
 9960        }
 9961    "});
 9962
 9963    cx.update(|cx| {
 9964        lsp_store.update(cx, |lsp_store, cx| {
 9965            lsp_store
 9966                .update_diagnostics(
 9967                    LanguageServerId(0),
 9968                    lsp::PublishDiagnosticsParams {
 9969                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9970                        version: None,
 9971                        diagnostics: vec![
 9972                            lsp::Diagnostic {
 9973                                range: lsp::Range::new(
 9974                                    lsp::Position::new(0, 11),
 9975                                    lsp::Position::new(0, 12),
 9976                                ),
 9977                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9978                                ..Default::default()
 9979                            },
 9980                            lsp::Diagnostic {
 9981                                range: lsp::Range::new(
 9982                                    lsp::Position::new(0, 12),
 9983                                    lsp::Position::new(0, 15),
 9984                                ),
 9985                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9986                                ..Default::default()
 9987                            },
 9988                            lsp::Diagnostic {
 9989                                range: lsp::Range::new(
 9990                                    lsp::Position::new(0, 25),
 9991                                    lsp::Position::new(0, 28),
 9992                                ),
 9993                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9994                                ..Default::default()
 9995                            },
 9996                        ],
 9997                    },
 9998                    &[],
 9999                    cx,
10000                )
10001                .unwrap()
10002        });
10003    });
10004
10005    executor.run_until_parked();
10006
10007    cx.update_editor(|editor, cx| {
10008        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10009    });
10010
10011    cx.assert_editor_state(indoc! {"
10012        fn func(abc def: i32) -> ˇu32 {
10013        }
10014    "});
10015
10016    cx.update_editor(|editor, cx| {
10017        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10018    });
10019
10020    cx.assert_editor_state(indoc! {"
10021        fn func(abc ˇdef: i32) -> u32 {
10022        }
10023    "});
10024
10025    cx.update_editor(|editor, cx| {
10026        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10027    });
10028
10029    cx.assert_editor_state(indoc! {"
10030        fn func(abcˇ def: i32) -> u32 {
10031        }
10032    "});
10033
10034    cx.update_editor(|editor, cx| {
10035        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10036    });
10037
10038    cx.assert_editor_state(indoc! {"
10039        fn func(abc def: i32) -> ˇu32 {
10040        }
10041    "});
10042}
10043
10044#[gpui::test]
10045async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10046    init_test(cx, |_| {});
10047
10048    let mut cx = EditorTestContext::new(cx).await;
10049
10050    cx.set_state(indoc! {"
10051        fn func(abˇc def: i32) -> u32 {
10052        }
10053    "});
10054    let lsp_store =
10055        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10056
10057    cx.update(|cx| {
10058        lsp_store.update(cx, |lsp_store, cx| {
10059            lsp_store.update_diagnostics(
10060                LanguageServerId(0),
10061                lsp::PublishDiagnosticsParams {
10062                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10063                    version: None,
10064                    diagnostics: vec![lsp::Diagnostic {
10065                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10066                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10067                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10068                        ..Default::default()
10069                    }],
10070                },
10071                &[],
10072                cx,
10073            )
10074        })
10075    }).unwrap();
10076    cx.run_until_parked();
10077    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10078    cx.run_until_parked();
10079    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10080}
10081
10082#[gpui::test]
10083async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10084    init_test(cx, |_| {});
10085
10086    let mut cx = EditorTestContext::new(cx).await;
10087
10088    let diff_base = r#"
10089        use some::mod;
10090
10091        const A: u32 = 42;
10092
10093        fn main() {
10094            println!("hello");
10095
10096            println!("world");
10097        }
10098        "#
10099    .unindent();
10100
10101    // Edits are modified, removed, modified, added
10102    cx.set_state(
10103        &r#"
10104        use some::modified;
10105
10106        ˇ
10107        fn main() {
10108            println!("hello there");
10109
10110            println!("around the");
10111            println!("world");
10112        }
10113        "#
10114        .unindent(),
10115    );
10116
10117    cx.set_diff_base(&diff_base);
10118    executor.run_until_parked();
10119
10120    cx.update_editor(|editor, cx| {
10121        //Wrap around the bottom of the buffer
10122        for _ in 0..3 {
10123            editor.go_to_next_hunk(&GoToHunk, cx);
10124        }
10125    });
10126
10127    cx.assert_editor_state(
10128        &r#"
10129        ˇuse some::modified;
10130
10131
10132        fn main() {
10133            println!("hello there");
10134
10135            println!("around the");
10136            println!("world");
10137        }
10138        "#
10139        .unindent(),
10140    );
10141
10142    cx.update_editor(|editor, cx| {
10143        //Wrap around the top of the buffer
10144        for _ in 0..2 {
10145            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10146        }
10147    });
10148
10149    cx.assert_editor_state(
10150        &r#"
10151        use some::modified;
10152
10153
10154        fn main() {
10155        ˇ    println!("hello there");
10156
10157            println!("around the");
10158            println!("world");
10159        }
10160        "#
10161        .unindent(),
10162    );
10163
10164    cx.update_editor(|editor, cx| {
10165        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10166    });
10167
10168    cx.assert_editor_state(
10169        &r#"
10170        use some::modified;
10171
10172        ˇ
10173        fn main() {
10174            println!("hello there");
10175
10176            println!("around the");
10177            println!("world");
10178        }
10179        "#
10180        .unindent(),
10181    );
10182
10183    cx.update_editor(|editor, cx| {
10184        for _ in 0..3 {
10185            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10186        }
10187    });
10188
10189    cx.assert_editor_state(
10190        &r#"
10191        use some::modified;
10192
10193
10194        fn main() {
10195        ˇ    println!("hello there");
10196
10197            println!("around the");
10198            println!("world");
10199        }
10200        "#
10201        .unindent(),
10202    );
10203
10204    cx.update_editor(|editor, cx| {
10205        editor.fold(&Fold, cx);
10206
10207        //Make sure that the fold only gets one hunk
10208        for _ in 0..4 {
10209            editor.go_to_next_hunk(&GoToHunk, cx);
10210        }
10211    });
10212
10213    cx.assert_editor_state(
10214        &r#"
10215        ˇuse some::modified;
10216
10217
10218        fn main() {
10219            println!("hello there");
10220
10221            println!("around the");
10222            println!("world");
10223        }
10224        "#
10225        .unindent(),
10226    );
10227}
10228
10229#[test]
10230fn test_split_words() {
10231    fn split(text: &str) -> Vec<&str> {
10232        split_words(text).collect()
10233    }
10234
10235    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10236    assert_eq!(split("hello_world"), &["hello_", "world"]);
10237    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10238    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10239    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10240    assert_eq!(split("helloworld"), &["helloworld"]);
10241
10242    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10243}
10244
10245#[gpui::test]
10246async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10247    init_test(cx, |_| {});
10248
10249    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10250    let mut assert = |before, after| {
10251        let _state_context = cx.set_state(before);
10252        cx.update_editor(|editor, cx| {
10253            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10254        });
10255        cx.assert_editor_state(after);
10256    };
10257
10258    // Outside bracket jumps to outside of matching bracket
10259    assert("console.logˇ(var);", "console.log(var)ˇ;");
10260    assert("console.log(var)ˇ;", "console.logˇ(var);");
10261
10262    // Inside bracket jumps to inside of matching bracket
10263    assert("console.log(ˇvar);", "console.log(varˇ);");
10264    assert("console.log(varˇ);", "console.log(ˇvar);");
10265
10266    // When outside a bracket and inside, favor jumping to the inside bracket
10267    assert(
10268        "console.log('foo', [1, 2, 3]ˇ);",
10269        "console.log(ˇ'foo', [1, 2, 3]);",
10270    );
10271    assert(
10272        "console.log(ˇ'foo', [1, 2, 3]);",
10273        "console.log('foo', [1, 2, 3]ˇ);",
10274    );
10275
10276    // Bias forward if two options are equally likely
10277    assert(
10278        "let result = curried_fun()ˇ();",
10279        "let result = curried_fun()()ˇ;",
10280    );
10281
10282    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10283    assert(
10284        indoc! {"
10285            function test() {
10286                console.log('test')ˇ
10287            }"},
10288        indoc! {"
10289            function test() {
10290                console.logˇ('test')
10291            }"},
10292    );
10293}
10294
10295#[gpui::test]
10296async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10297    init_test(cx, |_| {});
10298
10299    let fs = FakeFs::new(cx.executor());
10300    fs.insert_tree(
10301        "/a",
10302        json!({
10303            "main.rs": "fn main() { let a = 5; }",
10304            "other.rs": "// Test file",
10305        }),
10306    )
10307    .await;
10308    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10309
10310    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10311    language_registry.add(Arc::new(Language::new(
10312        LanguageConfig {
10313            name: "Rust".into(),
10314            matcher: LanguageMatcher {
10315                path_suffixes: vec!["rs".to_string()],
10316                ..Default::default()
10317            },
10318            brackets: BracketPairConfig {
10319                pairs: vec![BracketPair {
10320                    start: "{".to_string(),
10321                    end: "}".to_string(),
10322                    close: true,
10323                    surround: true,
10324                    newline: true,
10325                }],
10326                disabled_scopes_by_bracket_ix: Vec::new(),
10327            },
10328            ..Default::default()
10329        },
10330        Some(tree_sitter_rust::LANGUAGE.into()),
10331    )));
10332    let mut fake_servers = language_registry.register_fake_lsp(
10333        "Rust",
10334        FakeLspAdapter {
10335            capabilities: lsp::ServerCapabilities {
10336                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10337                    first_trigger_character: "{".to_string(),
10338                    more_trigger_character: None,
10339                }),
10340                ..Default::default()
10341            },
10342            ..Default::default()
10343        },
10344    );
10345
10346    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10347
10348    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10349
10350    let worktree_id = workspace
10351        .update(cx, |workspace, cx| {
10352            workspace.project().update(cx, |project, cx| {
10353                project.worktrees(cx).next().unwrap().read(cx).id()
10354            })
10355        })
10356        .unwrap();
10357
10358    let buffer = project
10359        .update(cx, |project, cx| {
10360            project.open_local_buffer("/a/main.rs", cx)
10361        })
10362        .await
10363        .unwrap();
10364    let editor_handle = workspace
10365        .update(cx, |workspace, cx| {
10366            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10367        })
10368        .unwrap()
10369        .await
10370        .unwrap()
10371        .downcast::<Editor>()
10372        .unwrap();
10373
10374    cx.executor().start_waiting();
10375    let fake_server = fake_servers.next().await.unwrap();
10376
10377    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10378        assert_eq!(
10379            params.text_document_position.text_document.uri,
10380            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10381        );
10382        assert_eq!(
10383            params.text_document_position.position,
10384            lsp::Position::new(0, 21),
10385        );
10386
10387        Ok(Some(vec![lsp::TextEdit {
10388            new_text: "]".to_string(),
10389            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10390        }]))
10391    });
10392
10393    editor_handle.update(cx, |editor, cx| {
10394        editor.focus(cx);
10395        editor.change_selections(None, cx, |s| {
10396            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10397        });
10398        editor.handle_input("{", cx);
10399    });
10400
10401    cx.executor().run_until_parked();
10402
10403    buffer.update(cx, |buffer, _| {
10404        assert_eq!(
10405            buffer.text(),
10406            "fn main() { let a = {5}; }",
10407            "No extra braces from on type formatting should appear in the buffer"
10408        )
10409    });
10410}
10411
10412#[gpui::test]
10413async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10414    init_test(cx, |_| {});
10415
10416    let fs = FakeFs::new(cx.executor());
10417    fs.insert_tree(
10418        "/a",
10419        json!({
10420            "main.rs": "fn main() { let a = 5; }",
10421            "other.rs": "// Test file",
10422        }),
10423    )
10424    .await;
10425
10426    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10427
10428    let server_restarts = Arc::new(AtomicUsize::new(0));
10429    let closure_restarts = Arc::clone(&server_restarts);
10430    let language_server_name = "test language server";
10431    let language_name: LanguageName = "Rust".into();
10432
10433    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10434    language_registry.add(Arc::new(Language::new(
10435        LanguageConfig {
10436            name: language_name.clone(),
10437            matcher: LanguageMatcher {
10438                path_suffixes: vec!["rs".to_string()],
10439                ..Default::default()
10440            },
10441            ..Default::default()
10442        },
10443        Some(tree_sitter_rust::LANGUAGE.into()),
10444    )));
10445    let mut fake_servers = language_registry.register_fake_lsp(
10446        "Rust",
10447        FakeLspAdapter {
10448            name: language_server_name,
10449            initialization_options: Some(json!({
10450                "testOptionValue": true
10451            })),
10452            initializer: Some(Box::new(move |fake_server| {
10453                let task_restarts = Arc::clone(&closure_restarts);
10454                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10455                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10456                    futures::future::ready(Ok(()))
10457                });
10458            })),
10459            ..Default::default()
10460        },
10461    );
10462
10463    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10464    let _buffer = project
10465        .update(cx, |project, cx| {
10466            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10467        })
10468        .await
10469        .unwrap();
10470    let _fake_server = fake_servers.next().await.unwrap();
10471    update_test_language_settings(cx, |language_settings| {
10472        language_settings.languages.insert(
10473            language_name.clone(),
10474            LanguageSettingsContent {
10475                tab_size: NonZeroU32::new(8),
10476                ..Default::default()
10477            },
10478        );
10479    });
10480    cx.executor().run_until_parked();
10481    assert_eq!(
10482        server_restarts.load(atomic::Ordering::Acquire),
10483        0,
10484        "Should not restart LSP server on an unrelated change"
10485    );
10486
10487    update_test_project_settings(cx, |project_settings| {
10488        project_settings.lsp.insert(
10489            "Some other server name".into(),
10490            LspSettings {
10491                binary: None,
10492                settings: None,
10493                initialization_options: Some(json!({
10494                    "some other init value": false
10495                })),
10496            },
10497        );
10498    });
10499    cx.executor().run_until_parked();
10500    assert_eq!(
10501        server_restarts.load(atomic::Ordering::Acquire),
10502        0,
10503        "Should not restart LSP server on an unrelated LSP settings change"
10504    );
10505
10506    update_test_project_settings(cx, |project_settings| {
10507        project_settings.lsp.insert(
10508            language_server_name.into(),
10509            LspSettings {
10510                binary: None,
10511                settings: None,
10512                initialization_options: Some(json!({
10513                    "anotherInitValue": false
10514                })),
10515            },
10516        );
10517    });
10518    cx.executor().run_until_parked();
10519    assert_eq!(
10520        server_restarts.load(atomic::Ordering::Acquire),
10521        1,
10522        "Should restart LSP server on a related LSP settings change"
10523    );
10524
10525    update_test_project_settings(cx, |project_settings| {
10526        project_settings.lsp.insert(
10527            language_server_name.into(),
10528            LspSettings {
10529                binary: None,
10530                settings: None,
10531                initialization_options: Some(json!({
10532                    "anotherInitValue": false
10533                })),
10534            },
10535        );
10536    });
10537    cx.executor().run_until_parked();
10538    assert_eq!(
10539        server_restarts.load(atomic::Ordering::Acquire),
10540        1,
10541        "Should not restart LSP server on a related LSP settings change that is the same"
10542    );
10543
10544    update_test_project_settings(cx, |project_settings| {
10545        project_settings.lsp.insert(
10546            language_server_name.into(),
10547            LspSettings {
10548                binary: None,
10549                settings: None,
10550                initialization_options: None,
10551            },
10552        );
10553    });
10554    cx.executor().run_until_parked();
10555    assert_eq!(
10556        server_restarts.load(atomic::Ordering::Acquire),
10557        2,
10558        "Should restart LSP server on another related LSP settings change"
10559    );
10560}
10561
10562#[gpui::test]
10563async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10564    init_test(cx, |_| {});
10565
10566    let mut cx = EditorLspTestContext::new_rust(
10567        lsp::ServerCapabilities {
10568            completion_provider: Some(lsp::CompletionOptions {
10569                trigger_characters: Some(vec![".".to_string()]),
10570                resolve_provider: Some(true),
10571                ..Default::default()
10572            }),
10573            ..Default::default()
10574        },
10575        cx,
10576    )
10577    .await;
10578
10579    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10580    cx.simulate_keystroke(".");
10581    let completion_item = lsp::CompletionItem {
10582        label: "some".into(),
10583        kind: Some(lsp::CompletionItemKind::SNIPPET),
10584        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10585        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10586            kind: lsp::MarkupKind::Markdown,
10587            value: "```rust\nSome(2)\n```".to_string(),
10588        })),
10589        deprecated: Some(false),
10590        sort_text: Some("fffffff2".to_string()),
10591        filter_text: Some("some".to_string()),
10592        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10593        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10594            range: lsp::Range {
10595                start: lsp::Position {
10596                    line: 0,
10597                    character: 22,
10598                },
10599                end: lsp::Position {
10600                    line: 0,
10601                    character: 22,
10602                },
10603            },
10604            new_text: "Some(2)".to_string(),
10605        })),
10606        additional_text_edits: Some(vec![lsp::TextEdit {
10607            range: lsp::Range {
10608                start: lsp::Position {
10609                    line: 0,
10610                    character: 20,
10611                },
10612                end: lsp::Position {
10613                    line: 0,
10614                    character: 22,
10615                },
10616            },
10617            new_text: "".to_string(),
10618        }]),
10619        ..Default::default()
10620    };
10621
10622    let closure_completion_item = completion_item.clone();
10623    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10624        let task_completion_item = closure_completion_item.clone();
10625        async move {
10626            Ok(Some(lsp::CompletionResponse::Array(vec![
10627                task_completion_item,
10628            ])))
10629        }
10630    });
10631
10632    request.next().await;
10633
10634    cx.condition(|editor, _| editor.context_menu_visible())
10635        .await;
10636    let apply_additional_edits = cx.update_editor(|editor, cx| {
10637        editor
10638            .confirm_completion(&ConfirmCompletion::default(), cx)
10639            .unwrap()
10640    });
10641    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10642
10643    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10644        let task_completion_item = completion_item.clone();
10645        async move { Ok(task_completion_item) }
10646    })
10647    .next()
10648    .await
10649    .unwrap();
10650    apply_additional_edits.await.unwrap();
10651    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10652}
10653
10654#[gpui::test]
10655async fn test_completions_resolve_updates_labels(cx: &mut gpui::TestAppContext) {
10656    init_test(cx, |_| {});
10657
10658    let mut cx = EditorLspTestContext::new_rust(
10659        lsp::ServerCapabilities {
10660            completion_provider: Some(lsp::CompletionOptions {
10661                trigger_characters: Some(vec![".".to_string()]),
10662                resolve_provider: Some(true),
10663                ..Default::default()
10664            }),
10665            ..Default::default()
10666        },
10667        cx,
10668    )
10669    .await;
10670
10671    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10672    cx.simulate_keystroke(".");
10673
10674    let completion_item = lsp::CompletionItem {
10675        label: "unresolved".to_string(),
10676        detail: None,
10677        documentation: None,
10678        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10679            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10680            new_text: ".unresolved".to_string(),
10681        })),
10682        ..lsp::CompletionItem::default()
10683    };
10684
10685    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10686        let item = completion_item.clone();
10687        async move { Ok(Some(lsp::CompletionResponse::Array(vec![item]))) }
10688    })
10689    .next()
10690    .await;
10691
10692    cx.condition(|editor, _| editor.context_menu_visible())
10693        .await;
10694    cx.update_editor(|editor, _| {
10695        let context_menu = editor.context_menu.read();
10696        let context_menu = context_menu
10697            .as_ref()
10698            .expect("Should have the context menu deployed");
10699        match context_menu {
10700            CodeContextMenu::Completions(completions_menu) => {
10701                let completions = completions_menu.completions.read();
10702                assert_eq!(completions.len(), 1, "Should have one completion");
10703                assert_eq!(completions.get(0).unwrap().label.text, "unresolved");
10704            }
10705            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10706        }
10707    });
10708
10709    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10710        Ok(lsp::CompletionItem {
10711            label: "resolved".to_string(),
10712            detail: Some("Now resolved!".to_string()),
10713            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10714            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10715                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10716                new_text: ".resolved".to_string(),
10717            })),
10718            ..lsp::CompletionItem::default()
10719        })
10720    })
10721    .next()
10722    .await;
10723    cx.run_until_parked();
10724
10725    cx.update_editor(|editor, _| {
10726        let context_menu = editor.context_menu.read();
10727        let context_menu = context_menu
10728            .as_ref()
10729            .expect("Should have the context menu deployed");
10730        match context_menu {
10731            CodeContextMenu::Completions(completions_menu) => {
10732                let completions = completions_menu.completions.read();
10733                assert_eq!(completions.len(), 1, "Should have one completion");
10734                assert_eq!(
10735                    completions.get(0).unwrap().label.text,
10736                    "resolved",
10737                    "Should update the completion label after resolving"
10738                );
10739            }
10740            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10741        }
10742    });
10743}
10744
10745#[gpui::test]
10746async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10747    init_test(cx, |_| {});
10748
10749    let item_0 = lsp::CompletionItem {
10750        label: "abs".into(),
10751        insert_text: Some("abs".into()),
10752        data: Some(json!({ "very": "special"})),
10753        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10754        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10755            lsp::InsertReplaceEdit {
10756                new_text: "abs".to_string(),
10757                insert: lsp::Range::default(),
10758                replace: lsp::Range::default(),
10759            },
10760        )),
10761        ..lsp::CompletionItem::default()
10762    };
10763    let items = iter::once(item_0.clone())
10764        .chain((11..51).map(|i| lsp::CompletionItem {
10765            label: format!("item_{}", i),
10766            insert_text: Some(format!("item_{}", i)),
10767            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10768            ..lsp::CompletionItem::default()
10769        }))
10770        .collect::<Vec<_>>();
10771
10772    let default_commit_characters = vec!["?".to_string()];
10773    let default_data = json!({ "default": "data"});
10774    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10775    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10776    let default_edit_range = lsp::Range {
10777        start: lsp::Position {
10778            line: 0,
10779            character: 5,
10780        },
10781        end: lsp::Position {
10782            line: 0,
10783            character: 5,
10784        },
10785    };
10786
10787    let item_0_out = lsp::CompletionItem {
10788        commit_characters: Some(default_commit_characters.clone()),
10789        insert_text_format: Some(default_insert_text_format),
10790        ..item_0
10791    };
10792    let items_out = iter::once(item_0_out)
10793        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
10794            commit_characters: Some(default_commit_characters.clone()),
10795            data: Some(default_data.clone()),
10796            insert_text_mode: Some(default_insert_text_mode),
10797            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10798                range: default_edit_range,
10799                new_text: item.label.clone(),
10800            })),
10801            ..item.clone()
10802        }))
10803        .collect::<Vec<lsp::CompletionItem>>();
10804
10805    let mut cx = EditorLspTestContext::new_rust(
10806        lsp::ServerCapabilities {
10807            completion_provider: Some(lsp::CompletionOptions {
10808                trigger_characters: Some(vec![".".to_string()]),
10809                resolve_provider: Some(true),
10810                ..Default::default()
10811            }),
10812            ..Default::default()
10813        },
10814        cx,
10815    )
10816    .await;
10817
10818    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10819    cx.simulate_keystroke(".");
10820
10821    let completion_data = default_data.clone();
10822    let completion_characters = default_commit_characters.clone();
10823    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10824        let default_data = completion_data.clone();
10825        let default_commit_characters = completion_characters.clone();
10826        let items = items.clone();
10827        async move {
10828            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10829                items,
10830                item_defaults: Some(lsp::CompletionListItemDefaults {
10831                    data: Some(default_data.clone()),
10832                    commit_characters: Some(default_commit_characters.clone()),
10833                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10834                        default_edit_range,
10835                    )),
10836                    insert_text_format: Some(default_insert_text_format),
10837                    insert_text_mode: Some(default_insert_text_mode),
10838                }),
10839                ..lsp::CompletionList::default()
10840            })))
10841        }
10842    })
10843    .next()
10844    .await;
10845
10846    let resolved_items = Arc::new(Mutex::new(Vec::new()));
10847    cx.lsp
10848        .server
10849        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10850            let closure_resolved_items = resolved_items.clone();
10851            move |item_to_resolve, _| {
10852                let closure_resolved_items = closure_resolved_items.clone();
10853                async move {
10854                    closure_resolved_items.lock().push(item_to_resolve.clone());
10855                    Ok(item_to_resolve)
10856                }
10857            }
10858        })
10859        .detach();
10860
10861    cx.condition(|editor, _| editor.context_menu_visible())
10862        .await;
10863    cx.run_until_parked();
10864    cx.update_editor(|editor, _| {
10865        let menu = editor.context_menu.read();
10866        match menu.as_ref().expect("should have the completions menu") {
10867            CodeContextMenu::Completions(completions_menu) => {
10868                assert_eq!(
10869                    completions_menu
10870                        .matches
10871                        .iter()
10872                        .map(|c| c.string.clone())
10873                        .collect::<Vec<String>>(),
10874                    items_out
10875                        .iter()
10876                        .map(|completion| completion.label.clone())
10877                        .collect::<Vec<String>>()
10878                );
10879            }
10880            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10881        }
10882    });
10883    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
10884    // with 4 from the end.
10885    assert_eq!(
10886        *resolved_items.lock(),
10887        [
10888            &items_out[0..16],
10889            &items_out[items_out.len() - 4..items_out.len()]
10890        ]
10891        .concat()
10892        .iter()
10893        .cloned()
10894        .collect::<Vec<lsp::CompletionItem>>()
10895    );
10896    resolved_items.lock().clear();
10897
10898    cx.update_editor(|editor, cx| {
10899        editor.context_menu_prev(&ContextMenuPrev, cx);
10900    });
10901    cx.run_until_parked();
10902    // Completions that have already been resolved are skipped.
10903    assert_eq!(
10904        *resolved_items.lock(),
10905        [
10906            // Selected item is always resolved even if it was resolved before.
10907            &items_out[items_out.len() - 1..items_out.len()],
10908            &items_out[items_out.len() - 16..items_out.len() - 4]
10909        ]
10910        .concat()
10911        .iter()
10912        .cloned()
10913        .collect::<Vec<lsp::CompletionItem>>()
10914    );
10915    resolved_items.lock().clear();
10916}
10917
10918#[gpui::test]
10919async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10920    init_test(cx, |_| {});
10921
10922    let mut cx = EditorLspTestContext::new(
10923        Language::new(
10924            LanguageConfig {
10925                matcher: LanguageMatcher {
10926                    path_suffixes: vec!["jsx".into()],
10927                    ..Default::default()
10928                },
10929                overrides: [(
10930                    "element".into(),
10931                    LanguageConfigOverride {
10932                        word_characters: Override::Set(['-'].into_iter().collect()),
10933                        ..Default::default()
10934                    },
10935                )]
10936                .into_iter()
10937                .collect(),
10938                ..Default::default()
10939            },
10940            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10941        )
10942        .with_override_query("(jsx_self_closing_element) @element")
10943        .unwrap(),
10944        lsp::ServerCapabilities {
10945            completion_provider: Some(lsp::CompletionOptions {
10946                trigger_characters: Some(vec![":".to_string()]),
10947                ..Default::default()
10948            }),
10949            ..Default::default()
10950        },
10951        cx,
10952    )
10953    .await;
10954
10955    cx.lsp
10956        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10957            Ok(Some(lsp::CompletionResponse::Array(vec![
10958                lsp::CompletionItem {
10959                    label: "bg-blue".into(),
10960                    ..Default::default()
10961                },
10962                lsp::CompletionItem {
10963                    label: "bg-red".into(),
10964                    ..Default::default()
10965                },
10966                lsp::CompletionItem {
10967                    label: "bg-yellow".into(),
10968                    ..Default::default()
10969                },
10970            ])))
10971        });
10972
10973    cx.set_state(r#"<p class="bgˇ" />"#);
10974
10975    // Trigger completion when typing a dash, because the dash is an extra
10976    // word character in the 'element' scope, which contains the cursor.
10977    cx.simulate_keystroke("-");
10978    cx.executor().run_until_parked();
10979    cx.update_editor(|editor, _| {
10980        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10981            assert_eq!(
10982                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10983                &["bg-red", "bg-blue", "bg-yellow"]
10984            );
10985        } else {
10986            panic!("expected completion menu to be open");
10987        }
10988    });
10989
10990    cx.simulate_keystroke("l");
10991    cx.executor().run_until_parked();
10992    cx.update_editor(|editor, _| {
10993        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10994            assert_eq!(
10995                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10996                &["bg-blue", "bg-yellow"]
10997            );
10998        } else {
10999            panic!("expected completion menu to be open");
11000        }
11001    });
11002
11003    // When filtering completions, consider the character after the '-' to
11004    // be the start of a subword.
11005    cx.set_state(r#"<p class="yelˇ" />"#);
11006    cx.simulate_keystroke("l");
11007    cx.executor().run_until_parked();
11008    cx.update_editor(|editor, _| {
11009        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
11010            assert_eq!(
11011                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
11012                &["bg-yellow"]
11013            );
11014        } else {
11015            panic!("expected completion menu to be open");
11016        }
11017    });
11018}
11019
11020#[gpui::test]
11021async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11022    init_test(cx, |settings| {
11023        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11024            FormatterList(vec![Formatter::Prettier].into()),
11025        ))
11026    });
11027
11028    let fs = FakeFs::new(cx.executor());
11029    fs.insert_file("/file.ts", Default::default()).await;
11030
11031    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11032    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11033
11034    language_registry.add(Arc::new(Language::new(
11035        LanguageConfig {
11036            name: "TypeScript".into(),
11037            matcher: LanguageMatcher {
11038                path_suffixes: vec!["ts".to_string()],
11039                ..Default::default()
11040            },
11041            ..Default::default()
11042        },
11043        Some(tree_sitter_rust::LANGUAGE.into()),
11044    )));
11045    update_test_language_settings(cx, |settings| {
11046        settings.defaults.prettier = Some(PrettierSettings {
11047            allowed: true,
11048            ..PrettierSettings::default()
11049        });
11050    });
11051
11052    let test_plugin = "test_plugin";
11053    let _ = language_registry.register_fake_lsp(
11054        "TypeScript",
11055        FakeLspAdapter {
11056            prettier_plugins: vec![test_plugin],
11057            ..Default::default()
11058        },
11059    );
11060
11061    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11062    let buffer = project
11063        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11064        .await
11065        .unwrap();
11066
11067    let buffer_text = "one\ntwo\nthree\n";
11068    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11069    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11070    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11071
11072    editor
11073        .update(cx, |editor, cx| {
11074            editor.perform_format(
11075                project.clone(),
11076                FormatTrigger::Manual,
11077                FormatTarget::Buffer,
11078                cx,
11079            )
11080        })
11081        .unwrap()
11082        .await;
11083    assert_eq!(
11084        editor.update(cx, |editor, cx| editor.text(cx)),
11085        buffer_text.to_string() + prettier_format_suffix,
11086        "Test prettier formatting was not applied to the original buffer text",
11087    );
11088
11089    update_test_language_settings(cx, |settings| {
11090        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11091    });
11092    let format = editor.update(cx, |editor, cx| {
11093        editor.perform_format(
11094            project.clone(),
11095            FormatTrigger::Manual,
11096            FormatTarget::Buffer,
11097            cx,
11098        )
11099    });
11100    format.await.unwrap();
11101    assert_eq!(
11102        editor.update(cx, |editor, cx| editor.text(cx)),
11103        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11104        "Autoformatting (via test prettier) was not applied to the original buffer text",
11105    );
11106}
11107
11108#[gpui::test]
11109async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11110    init_test(cx, |_| {});
11111    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11112    let base_text = indoc! {r#"
11113        struct Row;
11114        struct Row1;
11115        struct Row2;
11116
11117        struct Row4;
11118        struct Row5;
11119        struct Row6;
11120
11121        struct Row8;
11122        struct Row9;
11123        struct Row10;"#};
11124
11125    // When addition hunks are not adjacent to carets, no hunk revert is performed
11126    assert_hunk_revert(
11127        indoc! {r#"struct Row;
11128                   struct Row1;
11129                   struct Row1.1;
11130                   struct Row1.2;
11131                   struct Row2;ˇ
11132
11133                   struct Row4;
11134                   struct Row5;
11135                   struct Row6;
11136
11137                   struct Row8;
11138                   ˇstruct Row9;
11139                   struct Row9.1;
11140                   struct Row9.2;
11141                   struct Row9.3;
11142                   struct Row10;"#},
11143        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11144        indoc! {r#"struct Row;
11145                   struct Row1;
11146                   struct Row1.1;
11147                   struct Row1.2;
11148                   struct Row2;ˇ
11149
11150                   struct Row4;
11151                   struct Row5;
11152                   struct Row6;
11153
11154                   struct Row8;
11155                   ˇstruct Row9;
11156                   struct Row9.1;
11157                   struct Row9.2;
11158                   struct Row9.3;
11159                   struct Row10;"#},
11160        base_text,
11161        &mut cx,
11162    );
11163    // Same for selections
11164    assert_hunk_revert(
11165        indoc! {r#"struct Row;
11166                   struct Row1;
11167                   struct Row2;
11168                   struct Row2.1;
11169                   struct Row2.2;
11170                   «ˇ
11171                   struct Row4;
11172                   struct» Row5;
11173                   «struct Row6;
11174                   ˇ»
11175                   struct Row9.1;
11176                   struct Row9.2;
11177                   struct Row9.3;
11178                   struct Row8;
11179                   struct Row9;
11180                   struct Row10;"#},
11181        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11182        indoc! {r#"struct Row;
11183                   struct Row1;
11184                   struct Row2;
11185                   struct Row2.1;
11186                   struct Row2.2;
11187                   «ˇ
11188                   struct Row4;
11189                   struct» Row5;
11190                   «struct Row6;
11191                   ˇ»
11192                   struct Row9.1;
11193                   struct Row9.2;
11194                   struct Row9.3;
11195                   struct Row8;
11196                   struct Row9;
11197                   struct Row10;"#},
11198        base_text,
11199        &mut cx,
11200    );
11201
11202    // When carets and selections intersect the addition hunks, those are reverted.
11203    // Adjacent carets got merged.
11204    assert_hunk_revert(
11205        indoc! {r#"struct Row;
11206                   ˇ// something on the top
11207                   struct Row1;
11208                   struct Row2;
11209                   struct Roˇw3.1;
11210                   struct Row2.2;
11211                   struct Row2.3;ˇ
11212
11213                   struct Row4;
11214                   struct ˇRow5.1;
11215                   struct Row5.2;
11216                   struct «Rowˇ»5.3;
11217                   struct Row5;
11218                   struct Row6;
11219                   ˇ
11220                   struct Row9.1;
11221                   struct «Rowˇ»9.2;
11222                   struct «ˇRow»9.3;
11223                   struct Row8;
11224                   struct Row9;
11225                   «ˇ// something on bottom»
11226                   struct Row10;"#},
11227        vec![
11228            DiffHunkStatus::Added,
11229            DiffHunkStatus::Added,
11230            DiffHunkStatus::Added,
11231            DiffHunkStatus::Added,
11232            DiffHunkStatus::Added,
11233        ],
11234        indoc! {r#"struct Row;
11235                   ˇstruct Row1;
11236                   struct Row2;
11237                   ˇ
11238                   struct Row4;
11239                   ˇstruct Row5;
11240                   struct Row6;
11241                   ˇ
11242                   ˇstruct Row8;
11243                   struct Row9;
11244                   ˇstruct Row10;"#},
11245        base_text,
11246        &mut cx,
11247    );
11248}
11249
11250#[gpui::test]
11251async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11252    init_test(cx, |_| {});
11253    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11254    let base_text = indoc! {r#"
11255        struct Row;
11256        struct Row1;
11257        struct Row2;
11258
11259        struct Row4;
11260        struct Row5;
11261        struct Row6;
11262
11263        struct Row8;
11264        struct Row9;
11265        struct Row10;"#};
11266
11267    // Modification hunks behave the same as the addition ones.
11268    assert_hunk_revert(
11269        indoc! {r#"struct Row;
11270                   struct Row1;
11271                   struct Row33;
11272                   ˇ
11273                   struct Row4;
11274                   struct Row5;
11275                   struct Row6;
11276                   ˇ
11277                   struct Row99;
11278                   struct Row9;
11279                   struct Row10;"#},
11280        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11281        indoc! {r#"struct Row;
11282                   struct Row1;
11283                   struct Row33;
11284                   ˇ
11285                   struct Row4;
11286                   struct Row5;
11287                   struct Row6;
11288                   ˇ
11289                   struct Row99;
11290                   struct Row9;
11291                   struct Row10;"#},
11292        base_text,
11293        &mut cx,
11294    );
11295    assert_hunk_revert(
11296        indoc! {r#"struct Row;
11297                   struct Row1;
11298                   struct Row33;
11299                   «ˇ
11300                   struct Row4;
11301                   struct» Row5;
11302                   «struct Row6;
11303                   ˇ»
11304                   struct Row99;
11305                   struct Row9;
11306                   struct Row10;"#},
11307        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11308        indoc! {r#"struct Row;
11309                   struct Row1;
11310                   struct Row33;
11311                   «ˇ
11312                   struct Row4;
11313                   struct» Row5;
11314                   «struct Row6;
11315                   ˇ»
11316                   struct Row99;
11317                   struct Row9;
11318                   struct Row10;"#},
11319        base_text,
11320        &mut cx,
11321    );
11322
11323    assert_hunk_revert(
11324        indoc! {r#"ˇstruct Row1.1;
11325                   struct Row1;
11326                   «ˇstr»uct Row22;
11327
11328                   struct ˇRow44;
11329                   struct Row5;
11330                   struct «Rˇ»ow66;ˇ
11331
11332                   «struˇ»ct Row88;
11333                   struct Row9;
11334                   struct Row1011;ˇ"#},
11335        vec![
11336            DiffHunkStatus::Modified,
11337            DiffHunkStatus::Modified,
11338            DiffHunkStatus::Modified,
11339            DiffHunkStatus::Modified,
11340            DiffHunkStatus::Modified,
11341            DiffHunkStatus::Modified,
11342        ],
11343        indoc! {r#"struct Row;
11344                   ˇstruct Row1;
11345                   struct Row2;
11346                   ˇ
11347                   struct Row4;
11348                   ˇstruct Row5;
11349                   struct Row6;
11350                   ˇ
11351                   struct Row8;
11352                   ˇstruct Row9;
11353                   struct Row10;ˇ"#},
11354        base_text,
11355        &mut cx,
11356    );
11357}
11358
11359#[gpui::test]
11360async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11361    init_test(cx, |_| {});
11362    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11363    let base_text = indoc! {r#"struct Row;
11364struct Row1;
11365struct Row2;
11366
11367struct Row4;
11368struct Row5;
11369struct Row6;
11370
11371struct Row8;
11372struct Row9;
11373struct Row10;"#};
11374
11375    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11376    assert_hunk_revert(
11377        indoc! {r#"struct Row;
11378                   struct Row2;
11379
11380                   ˇstruct Row4;
11381                   struct Row5;
11382                   struct Row6;
11383                   ˇ
11384                   struct Row8;
11385                   struct Row10;"#},
11386        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11387        indoc! {r#"struct Row;
11388                   struct Row2;
11389
11390                   ˇstruct Row4;
11391                   struct Row5;
11392                   struct Row6;
11393                   ˇ
11394                   struct Row8;
11395                   struct Row10;"#},
11396        base_text,
11397        &mut cx,
11398    );
11399    assert_hunk_revert(
11400        indoc! {r#"struct Row;
11401                   struct Row2;
11402
11403                   «ˇstruct Row4;
11404                   struct» Row5;
11405                   «struct Row6;
11406                   ˇ»
11407                   struct Row8;
11408                   struct Row10;"#},
11409        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11410        indoc! {r#"struct Row;
11411                   struct Row2;
11412
11413                   «ˇstruct Row4;
11414                   struct» Row5;
11415                   «struct Row6;
11416                   ˇ»
11417                   struct Row8;
11418                   struct Row10;"#},
11419        base_text,
11420        &mut cx,
11421    );
11422
11423    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11424    assert_hunk_revert(
11425        indoc! {r#"struct Row;
11426                   ˇstruct Row2;
11427
11428                   struct Row4;
11429                   struct Row5;
11430                   struct Row6;
11431
11432                   struct Row8;ˇ
11433                   struct Row10;"#},
11434        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11435        indoc! {r#"struct Row;
11436                   struct Row1;
11437                   ˇstruct Row2;
11438
11439                   struct Row4;
11440                   struct Row5;
11441                   struct Row6;
11442
11443                   struct Row8;ˇ
11444                   struct Row9;
11445                   struct Row10;"#},
11446        base_text,
11447        &mut cx,
11448    );
11449    assert_hunk_revert(
11450        indoc! {r#"struct Row;
11451                   struct Row2«ˇ;
11452                   struct Row4;
11453                   struct» Row5;
11454                   «struct Row6;
11455
11456                   struct Row8;ˇ»
11457                   struct Row10;"#},
11458        vec![
11459            DiffHunkStatus::Removed,
11460            DiffHunkStatus::Removed,
11461            DiffHunkStatus::Removed,
11462        ],
11463        indoc! {r#"struct Row;
11464                   struct Row1;
11465                   struct Row2«ˇ;
11466
11467                   struct Row4;
11468                   struct» Row5;
11469                   «struct Row6;
11470
11471                   struct Row8;ˇ»
11472                   struct Row9;
11473                   struct Row10;"#},
11474        base_text,
11475        &mut cx,
11476    );
11477}
11478
11479#[gpui::test]
11480async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11481    init_test(cx, |_| {});
11482
11483    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11484    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11485    let base_text_3 =
11486        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11487
11488    let text_1 = edit_first_char_of_every_line(base_text_1);
11489    let text_2 = edit_first_char_of_every_line(base_text_2);
11490    let text_3 = edit_first_char_of_every_line(base_text_3);
11491
11492    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11493    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11494    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11495
11496    let multibuffer = cx.new_model(|cx| {
11497        let mut multibuffer = MultiBuffer::new(ReadWrite);
11498        multibuffer.push_excerpts(
11499            buffer_1.clone(),
11500            [
11501                ExcerptRange {
11502                    context: Point::new(0, 0)..Point::new(3, 0),
11503                    primary: None,
11504                },
11505                ExcerptRange {
11506                    context: Point::new(5, 0)..Point::new(7, 0),
11507                    primary: None,
11508                },
11509                ExcerptRange {
11510                    context: Point::new(9, 0)..Point::new(10, 4),
11511                    primary: None,
11512                },
11513            ],
11514            cx,
11515        );
11516        multibuffer.push_excerpts(
11517            buffer_2.clone(),
11518            [
11519                ExcerptRange {
11520                    context: Point::new(0, 0)..Point::new(3, 0),
11521                    primary: None,
11522                },
11523                ExcerptRange {
11524                    context: Point::new(5, 0)..Point::new(7, 0),
11525                    primary: None,
11526                },
11527                ExcerptRange {
11528                    context: Point::new(9, 0)..Point::new(10, 4),
11529                    primary: None,
11530                },
11531            ],
11532            cx,
11533        );
11534        multibuffer.push_excerpts(
11535            buffer_3.clone(),
11536            [
11537                ExcerptRange {
11538                    context: Point::new(0, 0)..Point::new(3, 0),
11539                    primary: None,
11540                },
11541                ExcerptRange {
11542                    context: Point::new(5, 0)..Point::new(7, 0),
11543                    primary: None,
11544                },
11545                ExcerptRange {
11546                    context: Point::new(9, 0)..Point::new(10, 4),
11547                    primary: None,
11548                },
11549            ],
11550            cx,
11551        );
11552        multibuffer
11553    });
11554
11555    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11556    editor.update(cx, |editor, cx| {
11557        for (buffer, diff_base) in [
11558            (buffer_1.clone(), base_text_1),
11559            (buffer_2.clone(), base_text_2),
11560            (buffer_3.clone(), base_text_3),
11561        ] {
11562            let change_set = cx.new_model(|cx| {
11563                BufferChangeSet::new_with_base_text(
11564                    diff_base.to_string(),
11565                    buffer.read(cx).text_snapshot(),
11566                    cx,
11567                )
11568            });
11569            editor.diff_map.add_change_set(change_set, cx)
11570        }
11571    });
11572    cx.executor().run_until_parked();
11573
11574    editor.update(cx, |editor, cx| {
11575        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}");
11576        editor.select_all(&SelectAll, cx);
11577        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11578    });
11579    cx.executor().run_until_parked();
11580
11581    // When all ranges are selected, all buffer hunks are reverted.
11582    editor.update(cx, |editor, cx| {
11583        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");
11584    });
11585    buffer_1.update(cx, |buffer, _| {
11586        assert_eq!(buffer.text(), base_text_1);
11587    });
11588    buffer_2.update(cx, |buffer, _| {
11589        assert_eq!(buffer.text(), base_text_2);
11590    });
11591    buffer_3.update(cx, |buffer, _| {
11592        assert_eq!(buffer.text(), base_text_3);
11593    });
11594
11595    editor.update(cx, |editor, cx| {
11596        editor.undo(&Default::default(), cx);
11597    });
11598
11599    editor.update(cx, |editor, cx| {
11600        editor.change_selections(None, cx, |s| {
11601            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11602        });
11603        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11604    });
11605
11606    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11607    // but not affect buffer_2 and its related excerpts.
11608    editor.update(cx, |editor, cx| {
11609        assert_eq!(
11610            editor.text(cx),
11611            "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}"
11612        );
11613    });
11614    buffer_1.update(cx, |buffer, _| {
11615        assert_eq!(buffer.text(), base_text_1);
11616    });
11617    buffer_2.update(cx, |buffer, _| {
11618        assert_eq!(
11619            buffer.text(),
11620            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11621        );
11622    });
11623    buffer_3.update(cx, |buffer, _| {
11624        assert_eq!(
11625            buffer.text(),
11626            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11627        );
11628    });
11629
11630    fn edit_first_char_of_every_line(text: &str) -> String {
11631        text.split('\n')
11632            .map(|line| format!("X{}", &line[1..]))
11633            .collect::<Vec<_>>()
11634            .join("\n")
11635    }
11636}
11637
11638#[gpui::test]
11639async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11640    init_test(cx, |_| {});
11641
11642    let cols = 4;
11643    let rows = 10;
11644    let sample_text_1 = sample_text(rows, cols, 'a');
11645    assert_eq!(
11646        sample_text_1,
11647        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11648    );
11649    let sample_text_2 = sample_text(rows, cols, 'l');
11650    assert_eq!(
11651        sample_text_2,
11652        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11653    );
11654    let sample_text_3 = sample_text(rows, cols, 'v');
11655    assert_eq!(
11656        sample_text_3,
11657        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11658    );
11659
11660    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11661    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11662    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11663
11664    let multi_buffer = cx.new_model(|cx| {
11665        let mut multibuffer = MultiBuffer::new(ReadWrite);
11666        multibuffer.push_excerpts(
11667            buffer_1.clone(),
11668            [
11669                ExcerptRange {
11670                    context: Point::new(0, 0)..Point::new(3, 0),
11671                    primary: None,
11672                },
11673                ExcerptRange {
11674                    context: Point::new(5, 0)..Point::new(7, 0),
11675                    primary: None,
11676                },
11677                ExcerptRange {
11678                    context: Point::new(9, 0)..Point::new(10, 4),
11679                    primary: None,
11680                },
11681            ],
11682            cx,
11683        );
11684        multibuffer.push_excerpts(
11685            buffer_2.clone(),
11686            [
11687                ExcerptRange {
11688                    context: Point::new(0, 0)..Point::new(3, 0),
11689                    primary: None,
11690                },
11691                ExcerptRange {
11692                    context: Point::new(5, 0)..Point::new(7, 0),
11693                    primary: None,
11694                },
11695                ExcerptRange {
11696                    context: Point::new(9, 0)..Point::new(10, 4),
11697                    primary: None,
11698                },
11699            ],
11700            cx,
11701        );
11702        multibuffer.push_excerpts(
11703            buffer_3.clone(),
11704            [
11705                ExcerptRange {
11706                    context: Point::new(0, 0)..Point::new(3, 0),
11707                    primary: None,
11708                },
11709                ExcerptRange {
11710                    context: Point::new(5, 0)..Point::new(7, 0),
11711                    primary: None,
11712                },
11713                ExcerptRange {
11714                    context: Point::new(9, 0)..Point::new(10, 4),
11715                    primary: None,
11716                },
11717            ],
11718            cx,
11719        );
11720        multibuffer
11721    });
11722
11723    let fs = FakeFs::new(cx.executor());
11724    fs.insert_tree(
11725        "/a",
11726        json!({
11727            "main.rs": sample_text_1,
11728            "other.rs": sample_text_2,
11729            "lib.rs": sample_text_3,
11730        }),
11731    )
11732    .await;
11733    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11734    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11735    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11736    let multi_buffer_editor = cx.new_view(|cx| {
11737        Editor::new(
11738            EditorMode::Full,
11739            multi_buffer,
11740            Some(project.clone()),
11741            true,
11742            cx,
11743        )
11744    });
11745    let multibuffer_item_id = workspace
11746        .update(cx, |workspace, cx| {
11747            assert!(
11748                workspace.active_item(cx).is_none(),
11749                "active item should be None before the first item is added"
11750            );
11751            workspace.add_item_to_active_pane(
11752                Box::new(multi_buffer_editor.clone()),
11753                None,
11754                true,
11755                cx,
11756            );
11757            let active_item = workspace
11758                .active_item(cx)
11759                .expect("should have an active item after adding the multi buffer");
11760            assert!(
11761                !active_item.is_singleton(cx),
11762                "A multi buffer was expected to active after adding"
11763            );
11764            active_item.item_id()
11765        })
11766        .unwrap();
11767    cx.executor().run_until_parked();
11768
11769    multi_buffer_editor.update(cx, |editor, cx| {
11770        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11771        editor.open_excerpts(&OpenExcerpts, cx);
11772    });
11773    cx.executor().run_until_parked();
11774    let first_item_id = workspace
11775        .update(cx, |workspace, cx| {
11776            let active_item = workspace
11777                .active_item(cx)
11778                .expect("should have an active item after navigating into the 1st buffer");
11779            let first_item_id = active_item.item_id();
11780            assert_ne!(
11781                first_item_id, multibuffer_item_id,
11782                "Should navigate into the 1st buffer and activate it"
11783            );
11784            assert!(
11785                active_item.is_singleton(cx),
11786                "New active item should be a singleton buffer"
11787            );
11788            assert_eq!(
11789                active_item
11790                    .act_as::<Editor>(cx)
11791                    .expect("should have navigated into an editor for the 1st buffer")
11792                    .read(cx)
11793                    .text(cx),
11794                sample_text_1
11795            );
11796
11797            workspace
11798                .go_back(workspace.active_pane().downgrade(), cx)
11799                .detach_and_log_err(cx);
11800
11801            first_item_id
11802        })
11803        .unwrap();
11804    cx.executor().run_until_parked();
11805    workspace
11806        .update(cx, |workspace, cx| {
11807            let active_item = workspace
11808                .active_item(cx)
11809                .expect("should have an active item after navigating back");
11810            assert_eq!(
11811                active_item.item_id(),
11812                multibuffer_item_id,
11813                "Should navigate back to the multi buffer"
11814            );
11815            assert!(!active_item.is_singleton(cx));
11816        })
11817        .unwrap();
11818
11819    multi_buffer_editor.update(cx, |editor, cx| {
11820        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11821            s.select_ranges(Some(39..40))
11822        });
11823        editor.open_excerpts(&OpenExcerpts, cx);
11824    });
11825    cx.executor().run_until_parked();
11826    let second_item_id = workspace
11827        .update(cx, |workspace, cx| {
11828            let active_item = workspace
11829                .active_item(cx)
11830                .expect("should have an active item after navigating into the 2nd buffer");
11831            let second_item_id = active_item.item_id();
11832            assert_ne!(
11833                second_item_id, multibuffer_item_id,
11834                "Should navigate away from the multibuffer"
11835            );
11836            assert_ne!(
11837                second_item_id, first_item_id,
11838                "Should navigate into the 2nd buffer and activate it"
11839            );
11840            assert!(
11841                active_item.is_singleton(cx),
11842                "New active item should be a singleton buffer"
11843            );
11844            assert_eq!(
11845                active_item
11846                    .act_as::<Editor>(cx)
11847                    .expect("should have navigated into an editor")
11848                    .read(cx)
11849                    .text(cx),
11850                sample_text_2
11851            );
11852
11853            workspace
11854                .go_back(workspace.active_pane().downgrade(), cx)
11855                .detach_and_log_err(cx);
11856
11857            second_item_id
11858        })
11859        .unwrap();
11860    cx.executor().run_until_parked();
11861    workspace
11862        .update(cx, |workspace, cx| {
11863            let active_item = workspace
11864                .active_item(cx)
11865                .expect("should have an active item after navigating back from the 2nd buffer");
11866            assert_eq!(
11867                active_item.item_id(),
11868                multibuffer_item_id,
11869                "Should navigate back from the 2nd buffer to the multi buffer"
11870            );
11871            assert!(!active_item.is_singleton(cx));
11872        })
11873        .unwrap();
11874
11875    multi_buffer_editor.update(cx, |editor, cx| {
11876        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11877            s.select_ranges(Some(70..70))
11878        });
11879        editor.open_excerpts(&OpenExcerpts, cx);
11880    });
11881    cx.executor().run_until_parked();
11882    workspace
11883        .update(cx, |workspace, cx| {
11884            let active_item = workspace
11885                .active_item(cx)
11886                .expect("should have an active item after navigating into the 3rd buffer");
11887            let third_item_id = active_item.item_id();
11888            assert_ne!(
11889                third_item_id, multibuffer_item_id,
11890                "Should navigate into the 3rd buffer and activate it"
11891            );
11892            assert_ne!(third_item_id, first_item_id);
11893            assert_ne!(third_item_id, second_item_id);
11894            assert!(
11895                active_item.is_singleton(cx),
11896                "New active item should be a singleton buffer"
11897            );
11898            assert_eq!(
11899                active_item
11900                    .act_as::<Editor>(cx)
11901                    .expect("should have navigated into an editor")
11902                    .read(cx)
11903                    .text(cx),
11904                sample_text_3
11905            );
11906
11907            workspace
11908                .go_back(workspace.active_pane().downgrade(), cx)
11909                .detach_and_log_err(cx);
11910        })
11911        .unwrap();
11912    cx.executor().run_until_parked();
11913    workspace
11914        .update(cx, |workspace, cx| {
11915            let active_item = workspace
11916                .active_item(cx)
11917                .expect("should have an active item after navigating back from the 3rd buffer");
11918            assert_eq!(
11919                active_item.item_id(),
11920                multibuffer_item_id,
11921                "Should navigate back from the 3rd buffer to the multi buffer"
11922            );
11923            assert!(!active_item.is_singleton(cx));
11924        })
11925        .unwrap();
11926}
11927
11928#[gpui::test]
11929async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11930    init_test(cx, |_| {});
11931
11932    let mut cx = EditorTestContext::new(cx).await;
11933
11934    let diff_base = r#"
11935        use some::mod;
11936
11937        const A: u32 = 42;
11938
11939        fn main() {
11940            println!("hello");
11941
11942            println!("world");
11943        }
11944        "#
11945    .unindent();
11946
11947    cx.set_state(
11948        &r#"
11949        use some::modified;
11950
11951        ˇ
11952        fn main() {
11953            println!("hello there");
11954
11955            println!("around the");
11956            println!("world");
11957        }
11958        "#
11959        .unindent(),
11960    );
11961
11962    cx.set_diff_base(&diff_base);
11963    executor.run_until_parked();
11964
11965    cx.update_editor(|editor, cx| {
11966        editor.go_to_next_hunk(&GoToHunk, cx);
11967        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11968    });
11969    executor.run_until_parked();
11970    cx.assert_state_with_diff(
11971        r#"
11972          use some::modified;
11973
11974
11975          fn main() {
11976        -     println!("hello");
11977        + ˇ    println!("hello there");
11978
11979              println!("around the");
11980              println!("world");
11981          }
11982        "#
11983        .unindent(),
11984    );
11985
11986    cx.update_editor(|editor, cx| {
11987        for _ in 0..3 {
11988            editor.go_to_next_hunk(&GoToHunk, cx);
11989            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11990        }
11991    });
11992    executor.run_until_parked();
11993    cx.assert_state_with_diff(
11994        r#"
11995        - use some::mod;
11996        + use some::modified;
11997
11998        - const A: u32 = 42;
11999          ˇ
12000          fn main() {
12001        -     println!("hello");
12002        +     println!("hello there");
12003
12004        +     println!("around the");
12005              println!("world");
12006          }
12007        "#
12008        .unindent(),
12009    );
12010
12011    cx.update_editor(|editor, cx| {
12012        editor.cancel(&Cancel, cx);
12013    });
12014
12015    cx.assert_state_with_diff(
12016        r#"
12017          use some::modified;
12018
12019          ˇ
12020          fn main() {
12021              println!("hello there");
12022
12023              println!("around the");
12024              println!("world");
12025          }
12026        "#
12027        .unindent(),
12028    );
12029}
12030
12031#[gpui::test]
12032async fn test_diff_base_change_with_expanded_diff_hunks(
12033    executor: BackgroundExecutor,
12034    cx: &mut gpui::TestAppContext,
12035) {
12036    init_test(cx, |_| {});
12037
12038    let mut cx = EditorTestContext::new(cx).await;
12039
12040    let diff_base = r#"
12041        use some::mod1;
12042        use some::mod2;
12043
12044        const A: u32 = 42;
12045        const B: u32 = 42;
12046        const C: u32 = 42;
12047
12048        fn main() {
12049            println!("hello");
12050
12051            println!("world");
12052        }
12053        "#
12054    .unindent();
12055
12056    cx.set_state(
12057        &r#"
12058        use some::mod2;
12059
12060        const A: u32 = 42;
12061        const C: u32 = 42;
12062
12063        fn main(ˇ) {
12064            //println!("hello");
12065
12066            println!("world");
12067            //
12068            //
12069        }
12070        "#
12071        .unindent(),
12072    );
12073
12074    cx.set_diff_base(&diff_base);
12075    executor.run_until_parked();
12076
12077    cx.update_editor(|editor, cx| {
12078        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12079    });
12080    executor.run_until_parked();
12081    cx.assert_state_with_diff(
12082        r#"
12083        - use some::mod1;
12084          use some::mod2;
12085
12086          const A: u32 = 42;
12087        - const B: u32 = 42;
12088          const C: u32 = 42;
12089
12090          fn main(ˇ) {
12091        -     println!("hello");
12092        +     //println!("hello");
12093
12094              println!("world");
12095        +     //
12096        +     //
12097          }
12098        "#
12099        .unindent(),
12100    );
12101
12102    cx.set_diff_base("new diff base!");
12103    executor.run_until_parked();
12104    cx.assert_state_with_diff(
12105        r#"
12106          use some::mod2;
12107
12108          const A: u32 = 42;
12109          const C: u32 = 42;
12110
12111          fn main(ˇ) {
12112              //println!("hello");
12113
12114              println!("world");
12115              //
12116              //
12117          }
12118        "#
12119        .unindent(),
12120    );
12121
12122    cx.update_editor(|editor, cx| {
12123        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12124    });
12125    executor.run_until_parked();
12126    cx.assert_state_with_diff(
12127        r#"
12128        - new diff base!
12129        + use some::mod2;
12130        +
12131        + const A: u32 = 42;
12132        + const C: u32 = 42;
12133        +
12134        + fn main(ˇ) {
12135        +     //println!("hello");
12136        +
12137        +     println!("world");
12138        +     //
12139        +     //
12140        + }
12141        "#
12142        .unindent(),
12143    );
12144}
12145
12146#[gpui::test]
12147async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12148    init_test(cx, |_| {});
12149
12150    let mut cx = EditorTestContext::new(cx).await;
12151
12152    let diff_base = r#"
12153        use some::mod1;
12154        use some::mod2;
12155
12156        const A: u32 = 42;
12157        const B: u32 = 42;
12158        const C: u32 = 42;
12159
12160        fn main() {
12161            println!("hello");
12162
12163            println!("world");
12164        }
12165
12166        fn another() {
12167            println!("another");
12168        }
12169
12170        fn another2() {
12171            println!("another2");
12172        }
12173        "#
12174    .unindent();
12175
12176    cx.set_state(
12177        &r#"
12178        «use some::mod2;
12179
12180        const A: u32 = 42;
12181        const C: u32 = 42;
12182
12183        fn main() {
12184            //println!("hello");
12185
12186            println!("world");
12187            //
12188            //ˇ»
12189        }
12190
12191        fn another() {
12192            println!("another");
12193            println!("another");
12194        }
12195
12196            println!("another2");
12197        }
12198        "#
12199        .unindent(),
12200    );
12201
12202    cx.set_diff_base(&diff_base);
12203    executor.run_until_parked();
12204
12205    cx.update_editor(|editor, cx| {
12206        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12207    });
12208    executor.run_until_parked();
12209
12210    cx.assert_state_with_diff(
12211        r#"
12212        - use some::mod1;
12213          «use some::mod2;
12214
12215          const A: u32 = 42;
12216        - const B: u32 = 42;
12217          const C: u32 = 42;
12218
12219          fn main() {
12220        -     println!("hello");
12221        +     //println!("hello");
12222
12223              println!("world");
12224        +     //
12225        +     //ˇ»
12226          }
12227
12228          fn another() {
12229              println!("another");
12230        +     println!("another");
12231          }
12232
12233        - fn another2() {
12234              println!("another2");
12235          }
12236        "#
12237        .unindent(),
12238    );
12239
12240    // Fold across some of the diff hunks. They should no longer appear expanded.
12241    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12242    cx.executor().run_until_parked();
12243
12244    // Hunks are not shown if their position is within a fold
12245    cx.assert_state_with_diff(
12246        r#"
12247          «use some::mod2;
12248
12249          const A: u32 = 42;
12250          const C: u32 = 42;
12251
12252          fn main() {
12253              //println!("hello");
12254
12255              println!("world");
12256              //
12257              //ˇ»
12258          }
12259
12260          fn another() {
12261              println!("another");
12262        +     println!("another");
12263          }
12264
12265        - fn another2() {
12266              println!("another2");
12267          }
12268        "#
12269        .unindent(),
12270    );
12271
12272    cx.update_editor(|editor, cx| {
12273        editor.select_all(&SelectAll, cx);
12274        editor.unfold_lines(&UnfoldLines, cx);
12275    });
12276    cx.executor().run_until_parked();
12277
12278    // The deletions reappear when unfolding.
12279    cx.assert_state_with_diff(
12280        r#"
12281        - use some::mod1;
12282          «use some::mod2;
12283
12284          const A: u32 = 42;
12285        - const B: u32 = 42;
12286          const C: u32 = 42;
12287
12288          fn main() {
12289        -     println!("hello");
12290        +     //println!("hello");
12291
12292              println!("world");
12293        +     //
12294        +     //
12295          }
12296
12297          fn another() {
12298              println!("another");
12299        +     println!("another");
12300          }
12301
12302        - fn another2() {
12303              println!("another2");
12304          }
12305          ˇ»"#
12306        .unindent(),
12307    );
12308}
12309
12310#[gpui::test]
12311async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12312    init_test(cx, |_| {});
12313
12314    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12315    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12316    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12317    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12318    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12319    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12320
12321    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12322    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12323    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12324
12325    let multi_buffer = cx.new_model(|cx| {
12326        let mut multibuffer = MultiBuffer::new(ReadWrite);
12327        multibuffer.push_excerpts(
12328            buffer_1.clone(),
12329            [
12330                ExcerptRange {
12331                    context: Point::new(0, 0)..Point::new(3, 0),
12332                    primary: None,
12333                },
12334                ExcerptRange {
12335                    context: Point::new(5, 0)..Point::new(7, 0),
12336                    primary: None,
12337                },
12338                ExcerptRange {
12339                    context: Point::new(9, 0)..Point::new(10, 3),
12340                    primary: None,
12341                },
12342            ],
12343            cx,
12344        );
12345        multibuffer.push_excerpts(
12346            buffer_2.clone(),
12347            [
12348                ExcerptRange {
12349                    context: Point::new(0, 0)..Point::new(3, 0),
12350                    primary: None,
12351                },
12352                ExcerptRange {
12353                    context: Point::new(5, 0)..Point::new(7, 0),
12354                    primary: None,
12355                },
12356                ExcerptRange {
12357                    context: Point::new(9, 0)..Point::new(10, 3),
12358                    primary: None,
12359                },
12360            ],
12361            cx,
12362        );
12363        multibuffer.push_excerpts(
12364            buffer_3.clone(),
12365            [
12366                ExcerptRange {
12367                    context: Point::new(0, 0)..Point::new(3, 0),
12368                    primary: None,
12369                },
12370                ExcerptRange {
12371                    context: Point::new(5, 0)..Point::new(7, 0),
12372                    primary: None,
12373                },
12374                ExcerptRange {
12375                    context: Point::new(9, 0)..Point::new(10, 3),
12376                    primary: None,
12377                },
12378            ],
12379            cx,
12380        );
12381        multibuffer
12382    });
12383
12384    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12385    editor
12386        .update(cx, |editor, cx| {
12387            for (buffer, diff_base) in [
12388                (buffer_1.clone(), file_1_old),
12389                (buffer_2.clone(), file_2_old),
12390                (buffer_3.clone(), file_3_old),
12391            ] {
12392                let change_set = cx.new_model(|cx| {
12393                    BufferChangeSet::new_with_base_text(
12394                        diff_base.to_string(),
12395                        buffer.read(cx).text_snapshot(),
12396                        cx,
12397                    )
12398                });
12399                editor.diff_map.add_change_set(change_set, cx)
12400            }
12401        })
12402        .unwrap();
12403
12404    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12405    cx.run_until_parked();
12406
12407    cx.assert_editor_state(
12408        &"
12409            ˇaaa
12410            ccc
12411            ddd
12412
12413            ggg
12414            hhh
12415
12416
12417            lll
12418            mmm
12419            NNN
12420
12421            qqq
12422            rrr
12423
12424            uuu
12425            111
12426            222
12427            333
12428
12429            666
12430            777
12431
12432            000
12433            !!!"
12434        .unindent(),
12435    );
12436
12437    cx.update_editor(|editor, cx| {
12438        editor.select_all(&SelectAll, cx);
12439        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12440    });
12441    cx.executor().run_until_parked();
12442
12443    cx.assert_state_with_diff(
12444        "
12445            «aaa
12446          - bbb
12447            ccc
12448            ddd
12449
12450            ggg
12451            hhh
12452
12453
12454            lll
12455            mmm
12456          - nnn
12457          + NNN
12458
12459            qqq
12460            rrr
12461
12462            uuu
12463            111
12464            222
12465            333
12466
12467          + 666
12468            777
12469
12470            000
12471            !!!ˇ»"
12472            .unindent(),
12473    );
12474}
12475
12476#[gpui::test]
12477async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12478    init_test(cx, |_| {});
12479
12480    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12481    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12482
12483    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12484    let multi_buffer = cx.new_model(|cx| {
12485        let mut multibuffer = MultiBuffer::new(ReadWrite);
12486        multibuffer.push_excerpts(
12487            buffer.clone(),
12488            [
12489                ExcerptRange {
12490                    context: Point::new(0, 0)..Point::new(2, 0),
12491                    primary: None,
12492                },
12493                ExcerptRange {
12494                    context: Point::new(5, 0)..Point::new(7, 0),
12495                    primary: None,
12496                },
12497            ],
12498            cx,
12499        );
12500        multibuffer
12501    });
12502
12503    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12504    editor
12505        .update(cx, |editor, cx| {
12506            let buffer = buffer.read(cx).text_snapshot();
12507            let change_set = cx
12508                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12509            editor.diff_map.add_change_set(change_set, cx)
12510        })
12511        .unwrap();
12512
12513    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12514    cx.run_until_parked();
12515
12516    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12517    cx.executor().run_until_parked();
12518
12519    cx.assert_state_with_diff(
12520        "
12521            ˇaaa
12522          - bbb
12523          + BBB
12524
12525          - ddd
12526          - eee
12527          + EEE
12528            fff
12529        "
12530        .unindent(),
12531    );
12532}
12533
12534#[gpui::test]
12535async fn test_edits_around_expanded_insertion_hunks(
12536    executor: BackgroundExecutor,
12537    cx: &mut gpui::TestAppContext,
12538) {
12539    init_test(cx, |_| {});
12540
12541    let mut cx = EditorTestContext::new(cx).await;
12542
12543    let diff_base = r#"
12544        use some::mod1;
12545        use some::mod2;
12546
12547        const A: u32 = 42;
12548
12549        fn main() {
12550            println!("hello");
12551
12552            println!("world");
12553        }
12554        "#
12555    .unindent();
12556    executor.run_until_parked();
12557    cx.set_state(
12558        &r#"
12559        use some::mod1;
12560        use some::mod2;
12561
12562        const A: u32 = 42;
12563        const B: u32 = 42;
12564        const C: u32 = 42;
12565        ˇ
12566
12567        fn main() {
12568            println!("hello");
12569
12570            println!("world");
12571        }
12572        "#
12573        .unindent(),
12574    );
12575
12576    cx.set_diff_base(&diff_base);
12577    executor.run_until_parked();
12578
12579    cx.update_editor(|editor, cx| {
12580        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12581    });
12582    executor.run_until_parked();
12583
12584    cx.assert_state_with_diff(
12585        r#"
12586        use some::mod1;
12587        use some::mod2;
12588
12589        const A: u32 = 42;
12590      + const B: u32 = 42;
12591      + const C: u32 = 42;
12592      + ˇ
12593
12594        fn main() {
12595            println!("hello");
12596
12597            println!("world");
12598        }
12599        "#
12600        .unindent(),
12601    );
12602
12603    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12604    executor.run_until_parked();
12605
12606    cx.assert_state_with_diff(
12607        r#"
12608        use some::mod1;
12609        use some::mod2;
12610
12611        const A: u32 = 42;
12612      + const B: u32 = 42;
12613      + const C: u32 = 42;
12614      + const D: u32 = 42;
12615      + ˇ
12616
12617        fn main() {
12618            println!("hello");
12619
12620            println!("world");
12621        }
12622        "#
12623        .unindent(),
12624    );
12625
12626    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12627    executor.run_until_parked();
12628
12629    cx.assert_state_with_diff(
12630        r#"
12631        use some::mod1;
12632        use some::mod2;
12633
12634        const A: u32 = 42;
12635      + const B: u32 = 42;
12636      + const C: u32 = 42;
12637      + const D: u32 = 42;
12638      + const E: u32 = 42;
12639      + ˇ
12640
12641        fn main() {
12642            println!("hello");
12643
12644            println!("world");
12645        }
12646        "#
12647        .unindent(),
12648    );
12649
12650    cx.update_editor(|editor, cx| {
12651        editor.delete_line(&DeleteLine, cx);
12652    });
12653    executor.run_until_parked();
12654
12655    cx.assert_state_with_diff(
12656        r#"
12657        use some::mod1;
12658        use some::mod2;
12659
12660        const A: u32 = 42;
12661      + const B: u32 = 42;
12662      + const C: u32 = 42;
12663      + const D: u32 = 42;
12664      + const E: u32 = 42;
12665        ˇ
12666        fn main() {
12667            println!("hello");
12668
12669            println!("world");
12670        }
12671        "#
12672        .unindent(),
12673    );
12674
12675    cx.update_editor(|editor, cx| {
12676        editor.move_up(&MoveUp, cx);
12677        editor.delete_line(&DeleteLine, cx);
12678        editor.move_up(&MoveUp, cx);
12679        editor.delete_line(&DeleteLine, cx);
12680        editor.move_up(&MoveUp, cx);
12681        editor.delete_line(&DeleteLine, cx);
12682    });
12683    executor.run_until_parked();
12684    cx.assert_state_with_diff(
12685        r#"
12686        use some::mod1;
12687        use some::mod2;
12688
12689        const A: u32 = 42;
12690      + const B: u32 = 42;
12691        ˇ
12692        fn main() {
12693            println!("hello");
12694
12695            println!("world");
12696        }
12697        "#
12698        .unindent(),
12699    );
12700
12701    cx.update_editor(|editor, cx| {
12702        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12703        editor.delete_line(&DeleteLine, cx);
12704    });
12705    executor.run_until_parked();
12706    cx.assert_state_with_diff(
12707        r#"
12708        use some::mod1;
12709      - use some::mod2;
12710      -
12711      - const A: u32 = 42;
12712        ˇ
12713        fn main() {
12714            println!("hello");
12715
12716            println!("world");
12717        }
12718        "#
12719        .unindent(),
12720    );
12721}
12722
12723#[gpui::test]
12724async fn test_edits_around_expanded_deletion_hunks(
12725    executor: BackgroundExecutor,
12726    cx: &mut gpui::TestAppContext,
12727) {
12728    init_test(cx, |_| {});
12729
12730    let mut cx = EditorTestContext::new(cx).await;
12731
12732    let diff_base = r#"
12733        use some::mod1;
12734        use some::mod2;
12735
12736        const A: u32 = 42;
12737        const B: u32 = 42;
12738        const C: u32 = 42;
12739
12740
12741        fn main() {
12742            println!("hello");
12743
12744            println!("world");
12745        }
12746    "#
12747    .unindent();
12748    executor.run_until_parked();
12749    cx.set_state(
12750        &r#"
12751        use some::mod1;
12752        use some::mod2;
12753
12754        ˇconst B: u32 = 42;
12755        const C: u32 = 42;
12756
12757
12758        fn main() {
12759            println!("hello");
12760
12761            println!("world");
12762        }
12763        "#
12764        .unindent(),
12765    );
12766
12767    cx.set_diff_base(&diff_base);
12768    executor.run_until_parked();
12769
12770    cx.update_editor(|editor, cx| {
12771        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12772    });
12773    executor.run_until_parked();
12774
12775    cx.assert_state_with_diff(
12776        r#"
12777        use some::mod1;
12778        use some::mod2;
12779
12780      - const A: u32 = 42;
12781        ˇconst B: u32 = 42;
12782        const C: u32 = 42;
12783
12784
12785        fn main() {
12786            println!("hello");
12787
12788            println!("world");
12789        }
12790        "#
12791        .unindent(),
12792    );
12793
12794    cx.update_editor(|editor, cx| {
12795        editor.delete_line(&DeleteLine, cx);
12796    });
12797    executor.run_until_parked();
12798    cx.assert_state_with_diff(
12799        r#"
12800        use some::mod1;
12801        use some::mod2;
12802
12803      - const A: u32 = 42;
12804      - const B: u32 = 42;
12805        ˇconst C: u32 = 42;
12806
12807
12808        fn main() {
12809            println!("hello");
12810
12811            println!("world");
12812        }
12813        "#
12814        .unindent(),
12815    );
12816
12817    cx.update_editor(|editor, cx| {
12818        editor.delete_line(&DeleteLine, cx);
12819    });
12820    executor.run_until_parked();
12821    cx.assert_state_with_diff(
12822        r#"
12823        use some::mod1;
12824        use some::mod2;
12825
12826      - const A: u32 = 42;
12827      - const B: u32 = 42;
12828      - const C: u32 = 42;
12829        ˇ
12830
12831        fn main() {
12832            println!("hello");
12833
12834            println!("world");
12835        }
12836        "#
12837        .unindent(),
12838    );
12839
12840    cx.update_editor(|editor, cx| {
12841        editor.handle_input("replacement", cx);
12842    });
12843    executor.run_until_parked();
12844    cx.assert_state_with_diff(
12845        r#"
12846        use some::mod1;
12847        use some::mod2;
12848
12849      - const A: u32 = 42;
12850      - const B: u32 = 42;
12851      - const C: u32 = 42;
12852      -
12853      + replacementˇ
12854
12855        fn main() {
12856            println!("hello");
12857
12858            println!("world");
12859        }
12860        "#
12861        .unindent(),
12862    );
12863}
12864
12865#[gpui::test]
12866async fn test_edit_after_expanded_modification_hunk(
12867    executor: BackgroundExecutor,
12868    cx: &mut gpui::TestAppContext,
12869) {
12870    init_test(cx, |_| {});
12871
12872    let mut cx = EditorTestContext::new(cx).await;
12873
12874    let diff_base = r#"
12875        use some::mod1;
12876        use some::mod2;
12877
12878        const A: u32 = 42;
12879        const B: u32 = 42;
12880        const C: u32 = 42;
12881        const D: u32 = 42;
12882
12883
12884        fn main() {
12885            println!("hello");
12886
12887            println!("world");
12888        }"#
12889    .unindent();
12890
12891    cx.set_state(
12892        &r#"
12893        use some::mod1;
12894        use some::mod2;
12895
12896        const A: u32 = 42;
12897        const B: u32 = 42;
12898        const C: u32 = 43ˇ
12899        const D: u32 = 42;
12900
12901
12902        fn main() {
12903            println!("hello");
12904
12905            println!("world");
12906        }"#
12907        .unindent(),
12908    );
12909
12910    cx.set_diff_base(&diff_base);
12911    executor.run_until_parked();
12912    cx.update_editor(|editor, cx| {
12913        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12914    });
12915    executor.run_until_parked();
12916
12917    cx.assert_state_with_diff(
12918        r#"
12919        use some::mod1;
12920        use some::mod2;
12921
12922        const A: u32 = 42;
12923        const B: u32 = 42;
12924      - const C: u32 = 42;
12925      + const C: u32 = 43ˇ
12926        const D: u32 = 42;
12927
12928
12929        fn main() {
12930            println!("hello");
12931
12932            println!("world");
12933        }"#
12934        .unindent(),
12935    );
12936
12937    cx.update_editor(|editor, cx| {
12938        editor.handle_input("\nnew_line\n", cx);
12939    });
12940    executor.run_until_parked();
12941
12942    cx.assert_state_with_diff(
12943        r#"
12944        use some::mod1;
12945        use some::mod2;
12946
12947        const A: u32 = 42;
12948        const B: u32 = 42;
12949      - const C: u32 = 42;
12950      + const C: u32 = 43
12951      + new_line
12952      + ˇ
12953        const D: u32 = 42;
12954
12955
12956        fn main() {
12957            println!("hello");
12958
12959            println!("world");
12960        }"#
12961        .unindent(),
12962    );
12963}
12964
12965async fn setup_indent_guides_editor(
12966    text: &str,
12967    cx: &mut gpui::TestAppContext,
12968) -> (BufferId, EditorTestContext) {
12969    init_test(cx, |_| {});
12970
12971    let mut cx = EditorTestContext::new(cx).await;
12972
12973    let buffer_id = cx.update_editor(|editor, cx| {
12974        editor.set_text(text, cx);
12975        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12976
12977        buffer_ids[0]
12978    });
12979
12980    (buffer_id, cx)
12981}
12982
12983fn assert_indent_guides(
12984    range: Range<u32>,
12985    expected: Vec<IndentGuide>,
12986    active_indices: Option<Vec<usize>>,
12987    cx: &mut EditorTestContext,
12988) {
12989    let indent_guides = cx.update_editor(|editor, cx| {
12990        let snapshot = editor.snapshot(cx).display_snapshot;
12991        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12992            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12993            true,
12994            &snapshot,
12995            cx,
12996        );
12997
12998        indent_guides.sort_by(|a, b| {
12999            a.depth.cmp(&b.depth).then(
13000                a.start_row
13001                    .cmp(&b.start_row)
13002                    .then(a.end_row.cmp(&b.end_row)),
13003            )
13004        });
13005        indent_guides
13006    });
13007
13008    if let Some(expected) = active_indices {
13009        let active_indices = cx.update_editor(|editor, cx| {
13010            let snapshot = editor.snapshot(cx).display_snapshot;
13011            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13012        });
13013
13014        assert_eq!(
13015            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13016            expected,
13017            "Active indent guide indices do not match"
13018        );
13019    }
13020
13021    let expected: Vec<_> = expected
13022        .into_iter()
13023        .map(|guide| MultiBufferIndentGuide {
13024            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13025            buffer: guide,
13026        })
13027        .collect();
13028
13029    assert_eq!(indent_guides, expected, "Indent guides do not match");
13030}
13031
13032fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13033    IndentGuide {
13034        buffer_id,
13035        start_row,
13036        end_row,
13037        depth,
13038        tab_size: 4,
13039        settings: IndentGuideSettings {
13040            enabled: true,
13041            line_width: 1,
13042            active_line_width: 1,
13043            ..Default::default()
13044        },
13045    }
13046}
13047
13048#[gpui::test]
13049async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13050    let (buffer_id, mut cx) = setup_indent_guides_editor(
13051        &"
13052    fn main() {
13053        let a = 1;
13054    }"
13055        .unindent(),
13056        cx,
13057    )
13058    .await;
13059
13060    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13061}
13062
13063#[gpui::test]
13064async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13065    let (buffer_id, mut cx) = setup_indent_guides_editor(
13066        &"
13067    fn main() {
13068        let a = 1;
13069        let b = 2;
13070    }"
13071        .unindent(),
13072        cx,
13073    )
13074    .await;
13075
13076    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13077}
13078
13079#[gpui::test]
13080async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13081    let (buffer_id, mut cx) = setup_indent_guides_editor(
13082        &"
13083    fn main() {
13084        let a = 1;
13085        if a == 3 {
13086            let b = 2;
13087        } else {
13088            let c = 3;
13089        }
13090    }"
13091        .unindent(),
13092        cx,
13093    )
13094    .await;
13095
13096    assert_indent_guides(
13097        0..8,
13098        vec![
13099            indent_guide(buffer_id, 1, 6, 0),
13100            indent_guide(buffer_id, 3, 3, 1),
13101            indent_guide(buffer_id, 5, 5, 1),
13102        ],
13103        None,
13104        &mut cx,
13105    );
13106}
13107
13108#[gpui::test]
13109async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13110    let (buffer_id, mut cx) = setup_indent_guides_editor(
13111        &"
13112    fn main() {
13113        let a = 1;
13114            let b = 2;
13115        let c = 3;
13116    }"
13117        .unindent(),
13118        cx,
13119    )
13120    .await;
13121
13122    assert_indent_guides(
13123        0..5,
13124        vec![
13125            indent_guide(buffer_id, 1, 3, 0),
13126            indent_guide(buffer_id, 2, 2, 1),
13127        ],
13128        None,
13129        &mut cx,
13130    );
13131}
13132
13133#[gpui::test]
13134async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13135    let (buffer_id, mut cx) = setup_indent_guides_editor(
13136        &"
13137        fn main() {
13138            let a = 1;
13139
13140            let c = 3;
13141        }"
13142        .unindent(),
13143        cx,
13144    )
13145    .await;
13146
13147    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13148}
13149
13150#[gpui::test]
13151async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13152    let (buffer_id, mut cx) = setup_indent_guides_editor(
13153        &"
13154        fn main() {
13155            let a = 1;
13156
13157            let c = 3;
13158
13159            if a == 3 {
13160                let b = 2;
13161            } else {
13162                let c = 3;
13163            }
13164        }"
13165        .unindent(),
13166        cx,
13167    )
13168    .await;
13169
13170    assert_indent_guides(
13171        0..11,
13172        vec![
13173            indent_guide(buffer_id, 1, 9, 0),
13174            indent_guide(buffer_id, 6, 6, 1),
13175            indent_guide(buffer_id, 8, 8, 1),
13176        ],
13177        None,
13178        &mut cx,
13179    );
13180}
13181
13182#[gpui::test]
13183async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13184    let (buffer_id, mut cx) = setup_indent_guides_editor(
13185        &"
13186        fn main() {
13187            let a = 1;
13188
13189            let c = 3;
13190
13191            if a == 3 {
13192                let b = 2;
13193            } else {
13194                let c = 3;
13195            }
13196        }"
13197        .unindent(),
13198        cx,
13199    )
13200    .await;
13201
13202    assert_indent_guides(
13203        1..11,
13204        vec![
13205            indent_guide(buffer_id, 1, 9, 0),
13206            indent_guide(buffer_id, 6, 6, 1),
13207            indent_guide(buffer_id, 8, 8, 1),
13208        ],
13209        None,
13210        &mut cx,
13211    );
13212}
13213
13214#[gpui::test]
13215async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13216    let (buffer_id, mut cx) = setup_indent_guides_editor(
13217        &"
13218        fn main() {
13219            let a = 1;
13220
13221            let c = 3;
13222
13223            if a == 3 {
13224                let b = 2;
13225            } else {
13226                let c = 3;
13227            }
13228        }"
13229        .unindent(),
13230        cx,
13231    )
13232    .await;
13233
13234    assert_indent_guides(
13235        1..10,
13236        vec![
13237            indent_guide(buffer_id, 1, 9, 0),
13238            indent_guide(buffer_id, 6, 6, 1),
13239            indent_guide(buffer_id, 8, 8, 1),
13240        ],
13241        None,
13242        &mut cx,
13243    );
13244}
13245
13246#[gpui::test]
13247async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13248    let (buffer_id, mut cx) = setup_indent_guides_editor(
13249        &"
13250        block1
13251            block2
13252                block3
13253                    block4
13254            block2
13255        block1
13256        block1"
13257            .unindent(),
13258        cx,
13259    )
13260    .await;
13261
13262    assert_indent_guides(
13263        1..10,
13264        vec![
13265            indent_guide(buffer_id, 1, 4, 0),
13266            indent_guide(buffer_id, 2, 3, 1),
13267            indent_guide(buffer_id, 3, 3, 2),
13268        ],
13269        None,
13270        &mut cx,
13271    );
13272}
13273
13274#[gpui::test]
13275async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13276    let (buffer_id, mut cx) = setup_indent_guides_editor(
13277        &"
13278        block1
13279            block2
13280                block3
13281
13282        block1
13283        block1"
13284            .unindent(),
13285        cx,
13286    )
13287    .await;
13288
13289    assert_indent_guides(
13290        0..6,
13291        vec![
13292            indent_guide(buffer_id, 1, 2, 0),
13293            indent_guide(buffer_id, 2, 2, 1),
13294        ],
13295        None,
13296        &mut cx,
13297    );
13298}
13299
13300#[gpui::test]
13301async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13302    let (buffer_id, mut cx) = setup_indent_guides_editor(
13303        &"
13304        block1
13305
13306
13307
13308            block2
13309        "
13310        .unindent(),
13311        cx,
13312    )
13313    .await;
13314
13315    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13316}
13317
13318#[gpui::test]
13319async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13320    let (buffer_id, mut cx) = setup_indent_guides_editor(
13321        &"
13322        def a:
13323        \tb = 3
13324        \tif True:
13325        \t\tc = 4
13326        \t\td = 5
13327        \tprint(b)
13328        "
13329        .unindent(),
13330        cx,
13331    )
13332    .await;
13333
13334    assert_indent_guides(
13335        0..6,
13336        vec![
13337            indent_guide(buffer_id, 1, 6, 0),
13338            indent_guide(buffer_id, 3, 4, 1),
13339        ],
13340        None,
13341        &mut cx,
13342    );
13343}
13344
13345#[gpui::test]
13346async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13347    let (buffer_id, mut cx) = setup_indent_guides_editor(
13348        &"
13349    fn main() {
13350        let a = 1;
13351    }"
13352        .unindent(),
13353        cx,
13354    )
13355    .await;
13356
13357    cx.update_editor(|editor, cx| {
13358        editor.change_selections(None, cx, |s| {
13359            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13360        });
13361    });
13362
13363    assert_indent_guides(
13364        0..3,
13365        vec![indent_guide(buffer_id, 1, 1, 0)],
13366        Some(vec![0]),
13367        &mut cx,
13368    );
13369}
13370
13371#[gpui::test]
13372async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13373    let (buffer_id, mut cx) = setup_indent_guides_editor(
13374        &"
13375    fn main() {
13376        if 1 == 2 {
13377            let a = 1;
13378        }
13379    }"
13380        .unindent(),
13381        cx,
13382    )
13383    .await;
13384
13385    cx.update_editor(|editor, cx| {
13386        editor.change_selections(None, cx, |s| {
13387            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13388        });
13389    });
13390
13391    assert_indent_guides(
13392        0..4,
13393        vec![
13394            indent_guide(buffer_id, 1, 3, 0),
13395            indent_guide(buffer_id, 2, 2, 1),
13396        ],
13397        Some(vec![1]),
13398        &mut cx,
13399    );
13400
13401    cx.update_editor(|editor, cx| {
13402        editor.change_selections(None, cx, |s| {
13403            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13404        });
13405    });
13406
13407    assert_indent_guides(
13408        0..4,
13409        vec![
13410            indent_guide(buffer_id, 1, 3, 0),
13411            indent_guide(buffer_id, 2, 2, 1),
13412        ],
13413        Some(vec![1]),
13414        &mut cx,
13415    );
13416
13417    cx.update_editor(|editor, cx| {
13418        editor.change_selections(None, cx, |s| {
13419            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13420        });
13421    });
13422
13423    assert_indent_guides(
13424        0..4,
13425        vec![
13426            indent_guide(buffer_id, 1, 3, 0),
13427            indent_guide(buffer_id, 2, 2, 1),
13428        ],
13429        Some(vec![0]),
13430        &mut cx,
13431    );
13432}
13433
13434#[gpui::test]
13435async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13436    let (buffer_id, mut cx) = setup_indent_guides_editor(
13437        &"
13438    fn main() {
13439        let a = 1;
13440
13441        let b = 2;
13442    }"
13443        .unindent(),
13444        cx,
13445    )
13446    .await;
13447
13448    cx.update_editor(|editor, cx| {
13449        editor.change_selections(None, cx, |s| {
13450            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13451        });
13452    });
13453
13454    assert_indent_guides(
13455        0..5,
13456        vec![indent_guide(buffer_id, 1, 3, 0)],
13457        Some(vec![0]),
13458        &mut cx,
13459    );
13460}
13461
13462#[gpui::test]
13463async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13464    let (buffer_id, mut cx) = setup_indent_guides_editor(
13465        &"
13466    def m:
13467        a = 1
13468        pass"
13469            .unindent(),
13470        cx,
13471    )
13472    .await;
13473
13474    cx.update_editor(|editor, cx| {
13475        editor.change_selections(None, cx, |s| {
13476            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13477        });
13478    });
13479
13480    assert_indent_guides(
13481        0..3,
13482        vec![indent_guide(buffer_id, 1, 2, 0)],
13483        Some(vec![0]),
13484        &mut cx,
13485    );
13486}
13487
13488#[gpui::test]
13489fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13490    init_test(cx, |_| {});
13491
13492    let editor = cx.add_window(|cx| {
13493        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13494        build_editor(buffer, cx)
13495    });
13496
13497    let render_args = Arc::new(Mutex::new(None));
13498    let snapshot = editor
13499        .update(cx, |editor, cx| {
13500            let snapshot = editor.buffer().read(cx).snapshot(cx);
13501            let range =
13502                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13503
13504            struct RenderArgs {
13505                row: MultiBufferRow,
13506                folded: bool,
13507                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13508            }
13509
13510            let crease = Crease::inline(
13511                range,
13512                FoldPlaceholder::test(),
13513                {
13514                    let toggle_callback = render_args.clone();
13515                    move |row, folded, callback, _cx| {
13516                        *toggle_callback.lock() = Some(RenderArgs {
13517                            row,
13518                            folded,
13519                            callback,
13520                        });
13521                        div()
13522                    }
13523                },
13524                |_row, _folded, _cx| div(),
13525            );
13526
13527            editor.insert_creases(Some(crease), cx);
13528            let snapshot = editor.snapshot(cx);
13529            let _div =
13530                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13531            snapshot
13532        })
13533        .unwrap();
13534
13535    let render_args = render_args.lock().take().unwrap();
13536    assert_eq!(render_args.row, MultiBufferRow(1));
13537    assert!(!render_args.folded);
13538    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13539
13540    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13541        .unwrap();
13542    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13543    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13544
13545    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13546        .unwrap();
13547    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13548    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13549}
13550
13551#[gpui::test]
13552async fn test_input_text(cx: &mut gpui::TestAppContext) {
13553    init_test(cx, |_| {});
13554    let mut cx = EditorTestContext::new(cx).await;
13555
13556    cx.set_state(
13557        &r#"ˇone
13558        two
13559
13560        three
13561        fourˇ
13562        five
13563
13564        siˇx"#
13565            .unindent(),
13566    );
13567
13568    cx.dispatch_action(HandleInput(String::new()));
13569    cx.assert_editor_state(
13570        &r#"ˇone
13571        two
13572
13573        three
13574        fourˇ
13575        five
13576
13577        siˇx"#
13578            .unindent(),
13579    );
13580
13581    cx.dispatch_action(HandleInput("AAAA".to_string()));
13582    cx.assert_editor_state(
13583        &r#"AAAAˇone
13584        two
13585
13586        three
13587        fourAAAAˇ
13588        five
13589
13590        siAAAAˇx"#
13591            .unindent(),
13592    );
13593}
13594
13595#[gpui::test]
13596async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13597    init_test(cx, |_| {});
13598
13599    let mut cx = EditorTestContext::new(cx).await;
13600    cx.set_state(
13601        r#"let foo = 1;
13602let foo = 2;
13603let foo = 3;
13604let fooˇ = 4;
13605let foo = 5;
13606let foo = 6;
13607let foo = 7;
13608let foo = 8;
13609let foo = 9;
13610let foo = 10;
13611let foo = 11;
13612let foo = 12;
13613let foo = 13;
13614let foo = 14;
13615let foo = 15;"#,
13616    );
13617
13618    cx.update_editor(|e, cx| {
13619        assert_eq!(
13620            e.next_scroll_position,
13621            NextScrollCursorCenterTopBottom::Center,
13622            "Default next scroll direction is center",
13623        );
13624
13625        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13626        assert_eq!(
13627            e.next_scroll_position,
13628            NextScrollCursorCenterTopBottom::Top,
13629            "After center, next scroll direction should be top",
13630        );
13631
13632        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13633        assert_eq!(
13634            e.next_scroll_position,
13635            NextScrollCursorCenterTopBottom::Bottom,
13636            "After top, next scroll direction should be bottom",
13637        );
13638
13639        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13640        assert_eq!(
13641            e.next_scroll_position,
13642            NextScrollCursorCenterTopBottom::Center,
13643            "After bottom, scrolling should start over",
13644        );
13645
13646        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13647        assert_eq!(
13648            e.next_scroll_position,
13649            NextScrollCursorCenterTopBottom::Top,
13650            "Scrolling continues if retriggered fast enough"
13651        );
13652    });
13653
13654    cx.executor()
13655        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13656    cx.executor().run_until_parked();
13657    cx.update_editor(|e, _| {
13658        assert_eq!(
13659            e.next_scroll_position,
13660            NextScrollCursorCenterTopBottom::Center,
13661            "If scrolling is not triggered fast enough, it should reset"
13662        );
13663    });
13664}
13665
13666#[gpui::test]
13667async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13668    init_test(cx, |_| {});
13669    let mut cx = EditorLspTestContext::new_rust(
13670        lsp::ServerCapabilities {
13671            definition_provider: Some(lsp::OneOf::Left(true)),
13672            references_provider: Some(lsp::OneOf::Left(true)),
13673            ..lsp::ServerCapabilities::default()
13674        },
13675        cx,
13676    )
13677    .await;
13678
13679    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13680        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13681            move |params, _| async move {
13682                if empty_go_to_definition {
13683                    Ok(None)
13684                } else {
13685                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13686                        uri: params.text_document_position_params.text_document.uri,
13687                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13688                    })))
13689                }
13690            },
13691        );
13692        let references =
13693            cx.lsp
13694                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13695                    Ok(Some(vec![lsp::Location {
13696                        uri: params.text_document_position.text_document.uri,
13697                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13698                    }]))
13699                });
13700        (go_to_definition, references)
13701    };
13702
13703    cx.set_state(
13704        &r#"fn one() {
13705            let mut a = ˇtwo();
13706        }
13707
13708        fn two() {}"#
13709            .unindent(),
13710    );
13711    set_up_lsp_handlers(false, &mut cx);
13712    let navigated = cx
13713        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13714        .await
13715        .expect("Failed to navigate to definition");
13716    assert_eq!(
13717        navigated,
13718        Navigated::Yes,
13719        "Should have navigated to definition from the GetDefinition response"
13720    );
13721    cx.assert_editor_state(
13722        &r#"fn one() {
13723            let mut a = two();
13724        }
13725
13726        fn «twoˇ»() {}"#
13727            .unindent(),
13728    );
13729
13730    let editors = cx.update_workspace(|workspace, cx| {
13731        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13732    });
13733    cx.update_editor(|_, test_editor_cx| {
13734        assert_eq!(
13735            editors.len(),
13736            1,
13737            "Initially, only one, test, editor should be open in the workspace"
13738        );
13739        assert_eq!(
13740            test_editor_cx.view(),
13741            editors.last().expect("Asserted len is 1")
13742        );
13743    });
13744
13745    set_up_lsp_handlers(true, &mut cx);
13746    let navigated = cx
13747        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13748        .await
13749        .expect("Failed to navigate to lookup references");
13750    assert_eq!(
13751        navigated,
13752        Navigated::Yes,
13753        "Should have navigated to references as a fallback after empty GoToDefinition response"
13754    );
13755    // We should not change the selections in the existing file,
13756    // if opening another milti buffer with the references
13757    cx.assert_editor_state(
13758        &r#"fn one() {
13759            let mut a = two();
13760        }
13761
13762        fn «twoˇ»() {}"#
13763            .unindent(),
13764    );
13765    let editors = cx.update_workspace(|workspace, cx| {
13766        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13767    });
13768    cx.update_editor(|_, test_editor_cx| {
13769        assert_eq!(
13770            editors.len(),
13771            2,
13772            "After falling back to references search, we open a new editor with the results"
13773        );
13774        let references_fallback_text = editors
13775            .into_iter()
13776            .find(|new_editor| new_editor != test_editor_cx.view())
13777            .expect("Should have one non-test editor now")
13778            .read(test_editor_cx)
13779            .text(test_editor_cx);
13780        assert_eq!(
13781            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13782            "Should use the range from the references response and not the GoToDefinition one"
13783        );
13784    });
13785}
13786
13787#[gpui::test]
13788async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13789    init_test(cx, |_| {});
13790
13791    let language = Arc::new(Language::new(
13792        LanguageConfig::default(),
13793        Some(tree_sitter_rust::LANGUAGE.into()),
13794    ));
13795
13796    let text = r#"
13797        #[cfg(test)]
13798        mod tests() {
13799            #[test]
13800            fn runnable_1() {
13801                let a = 1;
13802            }
13803
13804            #[test]
13805            fn runnable_2() {
13806                let a = 1;
13807                let b = 2;
13808            }
13809        }
13810    "#
13811    .unindent();
13812
13813    let fs = FakeFs::new(cx.executor());
13814    fs.insert_file("/file.rs", Default::default()).await;
13815
13816    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13817    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13818    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13819    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13820    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13821
13822    let editor = cx.new_view(|cx| {
13823        Editor::new(
13824            EditorMode::Full,
13825            multi_buffer,
13826            Some(project.clone()),
13827            true,
13828            cx,
13829        )
13830    });
13831
13832    editor.update(cx, |editor, cx| {
13833        editor.tasks.insert(
13834            (buffer.read(cx).remote_id(), 3),
13835            RunnableTasks {
13836                templates: vec![],
13837                offset: MultiBufferOffset(43),
13838                column: 0,
13839                extra_variables: HashMap::default(),
13840                context_range: BufferOffset(43)..BufferOffset(85),
13841            },
13842        );
13843        editor.tasks.insert(
13844            (buffer.read(cx).remote_id(), 8),
13845            RunnableTasks {
13846                templates: vec![],
13847                offset: MultiBufferOffset(86),
13848                column: 0,
13849                extra_variables: HashMap::default(),
13850                context_range: BufferOffset(86)..BufferOffset(191),
13851            },
13852        );
13853
13854        // Test finding task when cursor is inside function body
13855        editor.change_selections(None, cx, |s| {
13856            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13857        });
13858        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13859        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13860
13861        // Test finding task when cursor is on function name
13862        editor.change_selections(None, cx, |s| {
13863            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13864        });
13865        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13866        assert_eq!(row, 8, "Should find task when cursor is on function name");
13867    });
13868}
13869
13870fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13871    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13872    point..point
13873}
13874
13875fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13876    let (text, ranges) = marked_text_ranges(marked_text, true);
13877    assert_eq!(view.text(cx), text);
13878    assert_eq!(
13879        view.selections.ranges(cx),
13880        ranges,
13881        "Assert selections are {}",
13882        marked_text
13883    );
13884}
13885
13886pub fn handle_signature_help_request(
13887    cx: &mut EditorLspTestContext,
13888    mocked_response: lsp::SignatureHelp,
13889) -> impl Future<Output = ()> {
13890    let mut request =
13891        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13892            let mocked_response = mocked_response.clone();
13893            async move { Ok(Some(mocked_response)) }
13894        });
13895
13896    async move {
13897        request.next().await;
13898    }
13899}
13900
13901/// Handle completion request passing a marked string specifying where the completion
13902/// should be triggered from using '|' character, what range should be replaced, and what completions
13903/// should be returned using '<' and '>' to delimit the range
13904pub fn handle_completion_request(
13905    cx: &mut EditorLspTestContext,
13906    marked_string: &str,
13907    completions: Vec<&'static str>,
13908    counter: Arc<AtomicUsize>,
13909) -> impl Future<Output = ()> {
13910    let complete_from_marker: TextRangeMarker = '|'.into();
13911    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13912    let (_, mut marked_ranges) = marked_text_ranges_by(
13913        marked_string,
13914        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13915    );
13916
13917    let complete_from_position =
13918        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13919    let replace_range =
13920        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13921
13922    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13923        let completions = completions.clone();
13924        counter.fetch_add(1, atomic::Ordering::Release);
13925        async move {
13926            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13927            assert_eq!(
13928                params.text_document_position.position,
13929                complete_from_position
13930            );
13931            Ok(Some(lsp::CompletionResponse::Array(
13932                completions
13933                    .iter()
13934                    .map(|completion_text| lsp::CompletionItem {
13935                        label: completion_text.to_string(),
13936                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13937                            range: replace_range,
13938                            new_text: completion_text.to_string(),
13939                        })),
13940                        ..Default::default()
13941                    })
13942                    .collect(),
13943            )))
13944        }
13945    });
13946
13947    async move {
13948        request.next().await;
13949    }
13950}
13951
13952fn handle_resolve_completion_request(
13953    cx: &mut EditorLspTestContext,
13954    edits: Option<Vec<(&'static str, &'static str)>>,
13955) -> impl Future<Output = ()> {
13956    let edits = edits.map(|edits| {
13957        edits
13958            .iter()
13959            .map(|(marked_string, new_text)| {
13960                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13961                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13962                lsp::TextEdit::new(replace_range, new_text.to_string())
13963            })
13964            .collect::<Vec<_>>()
13965    });
13966
13967    let mut request =
13968        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13969            let edits = edits.clone();
13970            async move {
13971                Ok(lsp::CompletionItem {
13972                    additional_text_edits: edits,
13973                    ..Default::default()
13974                })
13975            }
13976        });
13977
13978    async move {
13979        request.next().await;
13980    }
13981}
13982
13983pub(crate) fn update_test_language_settings(
13984    cx: &mut TestAppContext,
13985    f: impl Fn(&mut AllLanguageSettingsContent),
13986) {
13987    cx.update(|cx| {
13988        SettingsStore::update_global(cx, |store, cx| {
13989            store.update_user_settings::<AllLanguageSettings>(cx, f);
13990        });
13991    });
13992}
13993
13994pub(crate) fn update_test_project_settings(
13995    cx: &mut TestAppContext,
13996    f: impl Fn(&mut ProjectSettings),
13997) {
13998    cx.update(|cx| {
13999        SettingsStore::update_global(cx, |store, cx| {
14000            store.update_user_settings::<ProjectSettings>(cx, f);
14001        });
14002    });
14003}
14004
14005pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14006    cx.update(|cx| {
14007        assets::Assets.load_test_fonts(cx);
14008        let store = SettingsStore::test(cx);
14009        cx.set_global(store);
14010        theme::init(theme::LoadThemes::JustBase, cx);
14011        release_channel::init(SemanticVersion::default(), cx);
14012        client::init_settings(cx);
14013        language::init(cx);
14014        Project::init_settings(cx);
14015        workspace::init_settings(cx);
14016        crate::init(cx);
14017    });
14018
14019    update_test_language_settings(cx, f);
14020}
14021
14022#[track_caller]
14023fn assert_hunk_revert(
14024    not_reverted_text_with_selections: &str,
14025    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14026    expected_reverted_text_with_selections: &str,
14027    base_text: &str,
14028    cx: &mut EditorLspTestContext,
14029) {
14030    cx.set_state(not_reverted_text_with_selections);
14031    cx.set_diff_base(base_text);
14032    cx.executor().run_until_parked();
14033
14034    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14035        let snapshot = editor.snapshot(cx);
14036        let reverted_hunk_statuses = snapshot
14037            .diff_map
14038            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14039            .map(|hunk| hunk_status(&hunk))
14040            .collect::<Vec<_>>();
14041
14042        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14043        reverted_hunk_statuses
14044    });
14045    cx.executor().run_until_parked();
14046    cx.assert_editor_state(expected_reverted_text_with_selections);
14047    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14048}