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