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, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    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 project::FakeFs;
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic;
   35use std::sync::atomic::AtomicUsize;
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use unindent::Unindent;
   38use util::{
   39    assert_set_eq,
   40    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   41};
   42use workspace::{
   43    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   44    NavigationEntry, ViewId,
   45};
   46
   47#[gpui::test]
   48fn test_edit_events(cx: &mut TestAppContext) {
   49    init_test(cx, |_| {});
   50
   51    let buffer = cx.new_model(|cx| {
   52        let mut buffer = language::Buffer::local("123456", cx);
   53        buffer.set_group_interval(Duration::from_secs(1));
   54        buffer
   55    });
   56
   57    let events = Rc::new(RefCell::new(Vec::new()));
   58    let editor1 = cx.add_window({
   59        let events = events.clone();
   60        |cx| {
   61            let view = cx.view().clone();
   62            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   63                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   64                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   65                _ => {}
   66            })
   67            .detach();
   68            Editor::for_buffer(buffer.clone(), None, cx)
   69        }
   70    });
   71
   72    let editor2 = cx.add_window({
   73        let events = events.clone();
   74        |cx| {
   75            cx.subscribe(
   76                &cx.view().clone(),
   77                move |_, _, event: &EditorEvent, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor2", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, cx)
   87        }
   88    });
   89
   90    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   91
   92    // Mutating editor 1 will emit an `Edited` event only for that editor.
   93    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   94    assert_eq!(
   95        mem::take(&mut *events.borrow_mut()),
   96        [
   97            ("editor1", "edited"),
   98            ("editor1", "buffer edited"),
   99            ("editor2", "buffer edited"),
  100        ]
  101    );
  102
  103    // Mutating editor 2 will emit an `Edited` event only for that editor.
  104    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor2", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  137    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor2", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // No event is emitted when the mutation is a no-op.
  159    _ = editor2.update(cx, |editor, cx| {
  160        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  161
  162        editor.backspace(&Backspace, cx);
  163    });
  164    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  165}
  166
  167#[gpui::test]
  168fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  169    init_test(cx, |_| {});
  170
  171    let mut now = Instant::now();
  172    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  173    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  174    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  175    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  176
  177    _ = editor.update(cx, |editor, cx| {
  178        editor.start_transaction_at(now, cx);
  179        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  180
  181        editor.insert("cd", cx);
  182        editor.end_transaction_at(now, cx);
  183        assert_eq!(editor.text(cx), "12cd56");
  184        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  185
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  188        editor.insert("e", cx);
  189        editor.end_transaction_at(now, cx);
  190        assert_eq!(editor.text(cx), "12cde6");
  191        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  192
  193        now += group_interval + Duration::from_millis(1);
  194        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  195
  196        // Simulate an edit in another editor
  197        buffer.update(cx, |buffer, cx| {
  198            buffer.start_transaction_at(now, cx);
  199            buffer.edit([(0..1, "a")], None, cx);
  200            buffer.edit([(1..1, "b")], None, cx);
  201            buffer.end_transaction_at(now, cx);
  202        });
  203
  204        assert_eq!(editor.text(cx), "ab2cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  206
  207        // Last transaction happened past the group interval in a different editor.
  208        // Undo it individually and don't restore selections.
  209        editor.undo(&Undo, cx);
  210        assert_eq!(editor.text(cx), "12cde6");
  211        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  212
  213        // First two transactions happened within the group interval in this editor.
  214        // Undo them together and restore selections.
  215        editor.undo(&Undo, cx);
  216        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  217        assert_eq!(editor.text(cx), "123456");
  218        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  219
  220        // Redo the first two transactions together.
  221        editor.redo(&Redo, cx);
  222        assert_eq!(editor.text(cx), "12cde6");
  223        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  224
  225        // Redo the last transaction on its own.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  229
  230        // Test empty transactions.
  231        editor.start_transaction_at(now, cx);
  232        editor.end_transaction_at(now, cx);
  233        editor.undo(&Undo, cx);
  234        assert_eq!(editor.text(cx), "12cde6");
  235    });
  236}
  237
  238#[gpui::test]
  239fn test_ime_composition(cx: &mut TestAppContext) {
  240    init_test(cx, |_| {});
  241
  242    let buffer = cx.new_model(|cx| {
  243        let mut buffer = language::Buffer::local("abcde", cx);
  244        // Ensure automatic grouping doesn't occur.
  245        buffer.set_group_interval(Duration::ZERO);
  246        buffer
  247    });
  248
  249    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  250    cx.add_window(|cx| {
  251        let mut editor = build_editor(buffer.clone(), cx);
  252
  253        // Start a new IME composition.
  254        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  255        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  257        assert_eq!(editor.text(cx), "äbcde");
  258        assert_eq!(
  259            editor.marked_text_ranges(cx),
  260            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  261        );
  262
  263        // Finalize IME composition.
  264        editor.replace_text_in_range(None, "ā", cx);
  265        assert_eq!(editor.text(cx), "ābcde");
  266        assert_eq!(editor.marked_text_ranges(cx), None);
  267
  268        // IME composition edits are grouped and are undone/redone at once.
  269        editor.undo(&Default::default(), cx);
  270        assert_eq!(editor.text(cx), "abcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272        editor.redo(&Default::default(), cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  278        assert_eq!(
  279            editor.marked_text_ranges(cx),
  280            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  281        );
  282
  283        // Undoing during an IME composition cancels it.
  284        editor.undo(&Default::default(), cx);
  285        assert_eq!(editor.text(cx), "ābcde");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287
  288        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  289        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  290        assert_eq!(editor.text(cx), "ābcdè");
  291        assert_eq!(
  292            editor.marked_text_ranges(cx),
  293            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  294        );
  295
  296        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  297        editor.replace_text_in_range(Some(4..999), "ę", cx);
  298        assert_eq!(editor.text(cx), "ābcdę");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition with multiple cursors.
  302        editor.change_selections(None, cx, |s| {
  303            s.select_ranges([
  304                OffsetUtf16(1)..OffsetUtf16(1),
  305                OffsetUtf16(3)..OffsetUtf16(3),
  306                OffsetUtf16(5)..OffsetUtf16(5),
  307            ])
  308        });
  309        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  310        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  311        assert_eq!(
  312            editor.marked_text_ranges(cx),
  313            Some(vec![
  314                OffsetUtf16(0)..OffsetUtf16(3),
  315                OffsetUtf16(4)..OffsetUtf16(7),
  316                OffsetUtf16(8)..OffsetUtf16(11)
  317            ])
  318        );
  319
  320        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  321        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  322        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  323        assert_eq!(
  324            editor.marked_text_ranges(cx),
  325            Some(vec![
  326                OffsetUtf16(1)..OffsetUtf16(2),
  327                OffsetUtf16(5)..OffsetUtf16(6),
  328                OffsetUtf16(9)..OffsetUtf16(10)
  329            ])
  330        );
  331
  332        // Finalize IME composition with multiple cursors.
  333        editor.replace_text_in_range(Some(9..10), "2", cx);
  334        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  335        assert_eq!(editor.marked_text_ranges(cx), None);
  336
  337        editor
  338    });
  339}
  340
  341#[gpui::test]
  342fn test_selection_with_mouse(cx: &mut TestAppContext) {
  343    init_test(cx, |_| {});
  344
  345    let editor = cx.add_window(|cx| {
  346        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  347        build_editor(buffer, cx)
  348    });
  349
  350    _ = editor.update(cx, |view, cx| {
  351        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  352    });
  353    assert_eq!(
  354        editor
  355            .update(cx, |view, cx| view.selections.display_ranges(cx))
  356            .unwrap(),
  357        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  358    );
  359
  360    _ = editor.update(cx, |view, cx| {
  361        view.update_selection(
  362            DisplayPoint::new(DisplayRow(3), 3),
  363            0,
  364            gpui::Point::<f32>::default(),
  365            cx,
  366        );
  367    });
  368
  369    assert_eq!(
  370        editor
  371            .update(cx, |view, cx| view.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  374    );
  375
  376    _ = editor.update(cx, |view, cx| {
  377        view.update_selection(
  378            DisplayPoint::new(DisplayRow(1), 1),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            cx,
  382        );
  383    });
  384
  385    assert_eq!(
  386        editor
  387            .update(cx, |view, cx| view.selections.display_ranges(cx))
  388            .unwrap(),
  389        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  390    );
  391
  392    _ = editor.update(cx, |view, cx| {
  393        view.end_selection(cx);
  394        view.update_selection(
  395            DisplayPoint::new(DisplayRow(3), 3),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            cx,
  399        );
  400    });
  401
  402    assert_eq!(
  403        editor
  404            .update(cx, |view, cx| view.selections.display_ranges(cx))
  405            .unwrap(),
  406        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  407    );
  408
  409    _ = editor.update(cx, |view, cx| {
  410        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  411        view.update_selection(
  412            DisplayPoint::new(DisplayRow(0), 0),
  413            0,
  414            gpui::Point::<f32>::default(),
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |view, cx| view.selections.display_ranges(cx))
  422            .unwrap(),
  423        [
  424            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  425            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  426        ]
  427    );
  428
  429    _ = editor.update(cx, |view, cx| {
  430        view.end_selection(cx);
  431    });
  432
  433    assert_eq!(
  434        editor
  435            .update(cx, |view, cx| view.selections.display_ranges(cx))
  436            .unwrap(),
  437        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  438    );
  439}
  440
  441#[gpui::test]
  442fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  443    init_test(cx, |_| {});
  444
  445    let editor = cx.add_window(|cx| {
  446        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  447        build_editor(buffer, cx)
  448    });
  449
  450    _ = editor.update(cx, |view, cx| {
  451        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.end_selection(cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    assert_eq!(
  467        editor
  468            .update(cx, |view, cx| view.selections.display_ranges(cx))
  469            .unwrap(),
  470        [
  471            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  472            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  473        ]
  474    );
  475
  476    _ = editor.update(cx, |view, cx| {
  477        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  478    });
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.end_selection(cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |view, cx| view.selections.display_ranges(cx))
  487            .unwrap(),
  488        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  489    );
  490}
  491
  492#[gpui::test]
  493fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  494    init_test(cx, |_| {});
  495
  496    let view = cx.add_window(|cx| {
  497        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  498        build_editor(buffer, cx)
  499    });
  500
  501    _ = view.update(cx, |view, cx| {
  502        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  503        assert_eq!(
  504            view.selections.display_ranges(cx),
  505            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  506        );
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.update_selection(
  511            DisplayPoint::new(DisplayRow(3), 3),
  512            0,
  513            gpui::Point::<f32>::default(),
  514            cx,
  515        );
  516        assert_eq!(
  517            view.selections.display_ranges(cx),
  518            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  519        );
  520    });
  521
  522    _ = view.update(cx, |view, cx| {
  523        view.cancel(&Cancel, cx);
  524        view.update_selection(
  525            DisplayPoint::new(DisplayRow(1), 1),
  526            0,
  527            gpui::Point::<f32>::default(),
  528            cx,
  529        );
  530        assert_eq!(
  531            view.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  533        );
  534    });
  535}
  536
  537#[gpui::test]
  538fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  539    init_test(cx, |_| {});
  540
  541    let view = cx.add_window(|cx| {
  542        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  543        build_editor(buffer, cx)
  544    });
  545
  546    _ = view.update(cx, |view, cx| {
  547        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  548        assert_eq!(
  549            view.selections.display_ranges(cx),
  550            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  551        );
  552
  553        view.move_down(&Default::default(), cx);
  554        assert_eq!(
  555            view.selections.display_ranges(cx),
  556            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  557        );
  558
  559        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  560        assert_eq!(
  561            view.selections.display_ranges(cx),
  562            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  563        );
  564
  565        view.move_up(&Default::default(), cx);
  566        assert_eq!(
  567            view.selections.display_ranges(cx),
  568            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  569        );
  570    });
  571}
  572
  573#[gpui::test]
  574fn test_clone(cx: &mut TestAppContext) {
  575    init_test(cx, |_| {});
  576
  577    let (text, selection_ranges) = marked_text_ranges(
  578        indoc! {"
  579            one
  580            two
  581            threeˇ
  582            four
  583            fiveˇ
  584        "},
  585        true,
  586    );
  587
  588    let editor = cx.add_window(|cx| {
  589        let buffer = MultiBuffer::build_simple(&text, cx);
  590        build_editor(buffer, cx)
  591    });
  592
  593    _ = editor.update(cx, |editor, cx| {
  594        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  595        editor.fold_ranges(
  596            [
  597                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  598                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  599            ],
  600            true,
  601            cx,
  602        );
  603    });
  604
  605    let cloned_editor = editor
  606        .update(cx, |editor, cx| {
  607            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  608        })
  609        .unwrap()
  610        .unwrap();
  611
  612    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  613    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614
  615    assert_eq!(
  616        cloned_editor
  617            .update(cx, |e, cx| e.display_text(cx))
  618            .unwrap(),
  619        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  620    );
  621    assert_eq!(
  622        cloned_snapshot
  623            .folds_in_range(0..text.len())
  624            .collect::<Vec<_>>(),
  625        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  626    );
  627    assert_set_eq!(
  628        cloned_editor
  629            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  630            .unwrap(),
  631        editor
  632            .update(cx, |editor, cx| editor.selections.ranges(cx))
  633            .unwrap()
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |e, cx| e.selections.display_ranges(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |e, cx| e.selections.display_ranges(cx))
  641            .unwrap()
  642    );
  643}
  644
  645#[gpui::test]
  646async fn test_navigation_history(cx: &mut TestAppContext) {
  647    init_test(cx, |_| {});
  648
  649    use workspace::item::Item;
  650
  651    let fs = FakeFs::new(cx.executor());
  652    let project = Project::test(fs, [], cx).await;
  653    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  654    let pane = workspace
  655        .update(cx, |workspace, _| workspace.active_pane().clone())
  656        .unwrap();
  657
  658    _ = workspace.update(cx, |_v, cx| {
  659        cx.new_view(|cx| {
  660            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  661            let mut editor = build_editor(buffer.clone(), cx);
  662            let handle = cx.view();
  663            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  664
  665            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  666                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  667            }
  668
  669            // Move the cursor a small distance.
  670            // Nothing is added to the navigation history.
  671            editor.change_selections(None, cx, |s| {
  672                s.select_display_ranges([
  673                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  674                ])
  675            });
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  679                ])
  680            });
  681            assert!(pop_history(&mut editor, cx).is_none());
  682
  683            // Move the cursor a large distance.
  684            // The history can jump back to the previous position.
  685            editor.change_selections(None, cx, |s| {
  686                s.select_display_ranges([
  687                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  688                ])
  689            });
  690            let nav_entry = pop_history(&mut editor, cx).unwrap();
  691            editor.navigate(nav_entry.data.unwrap(), cx);
  692            assert_eq!(nav_entry.item.id(), cx.entity_id());
  693            assert_eq!(
  694                editor.selections.display_ranges(cx),
  695                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  696            );
  697            assert!(pop_history(&mut editor, cx).is_none());
  698
  699            // Move the cursor a small distance via the mouse.
  700            // Nothing is added to the navigation history.
  701            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  702            editor.end_selection(cx);
  703            assert_eq!(
  704                editor.selections.display_ranges(cx),
  705                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  706            );
  707            assert!(pop_history(&mut editor, cx).is_none());
  708
  709            // Move the cursor a large distance via the mouse.
  710            // The history can jump back to the previous position.
  711            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  712            editor.end_selection(cx);
  713            assert_eq!(
  714                editor.selections.display_ranges(cx),
  715                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  716            );
  717            let nav_entry = pop_history(&mut editor, cx).unwrap();
  718            editor.navigate(nav_entry.data.unwrap(), cx);
  719            assert_eq!(nav_entry.item.id(), cx.entity_id());
  720            assert_eq!(
  721                editor.selections.display_ranges(cx),
  722                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  723            );
  724            assert!(pop_history(&mut editor, cx).is_none());
  725
  726            // Set scroll position to check later
  727            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  728            let original_scroll_position = editor.scroll_manager.anchor();
  729
  730            // Jump to the end of the document and adjust scroll
  731            editor.move_to_end(&MoveToEnd, cx);
  732            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  733            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  734
  735            let nav_entry = pop_history(&mut editor, cx).unwrap();
  736            editor.navigate(nav_entry.data.unwrap(), cx);
  737            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  740            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  741            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  742            let invalid_point = Point::new(9999, 0);
  743            editor.navigate(
  744                Box::new(NavigationData {
  745                    cursor_anchor: invalid_anchor,
  746                    cursor_position: invalid_point,
  747                    scroll_anchor: ScrollAnchor {
  748                        anchor: invalid_anchor,
  749                        offset: Default::default(),
  750                    },
  751                    scroll_top_row: invalid_point.row,
  752                }),
  753                cx,
  754            );
  755            assert_eq!(
  756                editor.selections.display_ranges(cx),
  757                &[editor.max_point(cx)..editor.max_point(cx)]
  758            );
  759            assert_eq!(
  760                editor.scroll_position(cx),
  761                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  762            );
  763
  764            editor
  765        })
  766    });
  767}
  768
  769#[gpui::test]
  770fn test_cancel(cx: &mut TestAppContext) {
  771    init_test(cx, |_| {});
  772
  773    let view = cx.add_window(|cx| {
  774        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  775        build_editor(buffer, cx)
  776    });
  777
  778    _ = view.update(cx, |view, cx| {
  779        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  780        view.update_selection(
  781            DisplayPoint::new(DisplayRow(1), 1),
  782            0,
  783            gpui::Point::<f32>::default(),
  784            cx,
  785        );
  786        view.end_selection(cx);
  787
  788        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  789        view.update_selection(
  790            DisplayPoint::new(DisplayRow(0), 3),
  791            0,
  792            gpui::Point::<f32>::default(),
  793            cx,
  794        );
  795        view.end_selection(cx);
  796        assert_eq!(
  797            view.selections.display_ranges(cx),
  798            [
  799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  800                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  801            ]
  802        );
  803    });
  804
  805    _ = view.update(cx, |view, cx| {
  806        view.cancel(&Cancel, cx);
  807        assert_eq!(
  808            view.selections.display_ranges(cx),
  809            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  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(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820}
  821
  822#[gpui::test]
  823fn test_fold_action(cx: &mut TestAppContext) {
  824    init_test(cx, |_| {});
  825
  826    let view = cx.add_window(|cx| {
  827        let buffer = MultiBuffer::build_simple(
  828            &"
  829                impl Foo {
  830                    // Hello!
  831
  832                    fn a() {
  833                        1
  834                    }
  835
  836                    fn b() {
  837                        2
  838                    }
  839
  840                    fn c() {
  841                        3
  842                    }
  843                }
  844            "
  845            .unindent(),
  846            cx,
  847        );
  848        build_editor(buffer.clone(), cx)
  849    });
  850
  851    _ = view.update(cx, |view, cx| {
  852        view.change_selections(None, cx, |s| {
  853            s.select_display_ranges([
  854                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  855            ]);
  856        });
  857        view.fold(&Fold, cx);
  858        assert_eq!(
  859            view.display_text(cx),
  860            "
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {⋯
  869                    }
  870
  871                    fn c() {⋯
  872                    }
  873                }
  874            "
  875            .unindent(),
  876        );
  877
  878        view.fold(&Fold, cx);
  879        assert_eq!(
  880            view.display_text(cx),
  881            "
  882                impl Foo {⋯
  883                }
  884            "
  885            .unindent(),
  886        );
  887
  888        view.unfold_lines(&UnfoldLines, cx);
  889        assert_eq!(
  890            view.display_text(cx),
  891            "
  892                impl Foo {
  893                    // Hello!
  894
  895                    fn a() {
  896                        1
  897                    }
  898
  899                    fn b() {⋯
  900                    }
  901
  902                    fn c() {⋯
  903                    }
  904                }
  905            "
  906            .unindent(),
  907        );
  908
  909        view.unfold_lines(&UnfoldLines, cx);
  910        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  911    });
  912}
  913
  914#[gpui::test]
  915fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  916    init_test(cx, |_| {});
  917
  918    let view = cx.add_window(|cx| {
  919        let buffer = MultiBuffer::build_simple(
  920            &"
  921                class Foo:
  922                    # Hello!
  923
  924                    def a():
  925                        print(1)
  926
  927                    def b():
  928                        print(2)
  929
  930                    def c():
  931                        print(3)
  932            "
  933            .unindent(),
  934            cx,
  935        );
  936        build_editor(buffer.clone(), cx)
  937    });
  938
  939    _ = view.update(cx, |view, cx| {
  940        view.change_selections(None, cx, |s| {
  941            s.select_display_ranges([
  942                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  943            ]);
  944        });
  945        view.fold(&Fold, cx);
  946        assert_eq!(
  947            view.display_text(cx),
  948            "
  949                class Foo:
  950                    # Hello!
  951
  952                    def a():
  953                        print(1)
  954
  955                    def b():⋯
  956
  957                    def c():⋯
  958            "
  959            .unindent(),
  960        );
  961
  962        view.fold(&Fold, cx);
  963        assert_eq!(
  964            view.display_text(cx),
  965            "
  966                class Foo:⋯
  967            "
  968            .unindent(),
  969        );
  970
  971        view.unfold_lines(&UnfoldLines, cx);
  972        assert_eq!(
  973            view.display_text(cx),
  974            "
  975                class Foo:
  976                    # Hello!
  977
  978                    def a():
  979                        print(1)
  980
  981                    def b():⋯
  982
  983                    def c():⋯
  984            "
  985            .unindent(),
  986        );
  987
  988        view.unfold_lines(&UnfoldLines, cx);
  989        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  990    });
  991}
  992
  993#[gpui::test]
  994fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  995    init_test(cx, |_| {});
  996
  997    let view = cx.add_window(|cx| {
  998        let buffer = MultiBuffer::build_simple(
  999            &"
 1000                class Foo:
 1001                    # Hello!
 1002
 1003                    def a():
 1004                        print(1)
 1005
 1006                    def b():
 1007                        print(2)
 1008
 1009
 1010                    def c():
 1011                        print(3)
 1012
 1013
 1014            "
 1015            .unindent(),
 1016            cx,
 1017        );
 1018        build_editor(buffer.clone(), cx)
 1019    });
 1020
 1021    _ = view.update(cx, |view, cx| {
 1022        view.change_selections(None, cx, |s| {
 1023            s.select_display_ranges([
 1024                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1025            ]);
 1026        });
 1027        view.fold(&Fold, cx);
 1028        assert_eq!(
 1029            view.display_text(cx),
 1030            "
 1031                class Foo:
 1032                    # Hello!
 1033
 1034                    def a():
 1035                        print(1)
 1036
 1037                    def b():⋯
 1038
 1039
 1040                    def c():⋯
 1041
 1042
 1043            "
 1044            .unindent(),
 1045        );
 1046
 1047        view.fold(&Fold, cx);
 1048        assert_eq!(
 1049            view.display_text(cx),
 1050            "
 1051                class Foo:⋯
 1052
 1053
 1054            "
 1055            .unindent(),
 1056        );
 1057
 1058        view.unfold_lines(&UnfoldLines, cx);
 1059        assert_eq!(
 1060            view.display_text(cx),
 1061            "
 1062                class Foo:
 1063                    # Hello!
 1064
 1065                    def a():
 1066                        print(1)
 1067
 1068                    def b():⋯
 1069
 1070
 1071                    def c():⋯
 1072
 1073
 1074            "
 1075            .unindent(),
 1076        );
 1077
 1078        view.unfold_lines(&UnfoldLines, cx);
 1079        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1080    });
 1081}
 1082
 1083#[gpui::test]
 1084fn test_fold_at_level(cx: &mut TestAppContext) {
 1085    init_test(cx, |_| {});
 1086
 1087    let view = cx.add_window(|cx| {
 1088        let buffer = MultiBuffer::build_simple(
 1089            &"
 1090                class Foo:
 1091                    # Hello!
 1092
 1093                    def a():
 1094                        print(1)
 1095
 1096                    def b():
 1097                        print(2)
 1098
 1099
 1100                class Bar:
 1101                    # World!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():
 1107                        print(2)
 1108
 1109
 1110            "
 1111            .unindent(),
 1112            cx,
 1113        );
 1114        build_editor(buffer.clone(), cx)
 1115    });
 1116
 1117    _ = view.update(cx, |view, cx| {
 1118        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1119        assert_eq!(
 1120            view.display_text(cx),
 1121            "
 1122                class Foo:
 1123                    # Hello!
 1124
 1125                    def a():⋯
 1126
 1127                    def b():⋯
 1128
 1129
 1130                class Bar:
 1131                    # World!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138            "
 1139            .unindent(),
 1140        );
 1141
 1142        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1143        assert_eq!(
 1144            view.display_text(cx),
 1145            "
 1146                class Foo:⋯
 1147
 1148
 1149                class Bar:⋯
 1150
 1151
 1152            "
 1153            .unindent(),
 1154        );
 1155
 1156        view.unfold_all(&UnfoldAll, cx);
 1157        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1158        assert_eq!(
 1159            view.display_text(cx),
 1160            "
 1161                class Foo:
 1162                    # Hello!
 1163
 1164                    def a():
 1165                        print(1)
 1166
 1167                    def b():
 1168                        print(2)
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():
 1175                        print(1)
 1176
 1177                    def b():
 1178                        print(2)
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1186    });
 1187}
 1188
 1189#[gpui::test]
 1190fn test_move_cursor(cx: &mut TestAppContext) {
 1191    init_test(cx, |_| {});
 1192
 1193    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1194    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1195
 1196    buffer.update(cx, |buffer, cx| {
 1197        buffer.edit(
 1198            vec![
 1199                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1200                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1201            ],
 1202            None,
 1203            cx,
 1204        );
 1205    });
 1206    _ = view.update(cx, |view, cx| {
 1207        assert_eq!(
 1208            view.selections.display_ranges(cx),
 1209            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1210        );
 1211
 1212        view.move_down(&MoveDown, cx);
 1213        assert_eq!(
 1214            view.selections.display_ranges(cx),
 1215            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1216        );
 1217
 1218        view.move_right(&MoveRight, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1222        );
 1223
 1224        view.move_left(&MoveLeft, cx);
 1225        assert_eq!(
 1226            view.selections.display_ranges(cx),
 1227            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1228        );
 1229
 1230        view.move_up(&MoveUp, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1234        );
 1235
 1236        view.move_to_end(&MoveToEnd, cx);
 1237        assert_eq!(
 1238            view.selections.display_ranges(cx),
 1239            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1240        );
 1241
 1242        view.move_to_beginning(&MoveToBeginning, cx);
 1243        assert_eq!(
 1244            view.selections.display_ranges(cx),
 1245            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1246        );
 1247
 1248        view.change_selections(None, cx, |s| {
 1249            s.select_display_ranges([
 1250                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1251            ]);
 1252        });
 1253        view.select_to_beginning(&SelectToBeginning, cx);
 1254        assert_eq!(
 1255            view.selections.display_ranges(cx),
 1256            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1257        );
 1258
 1259        view.select_to_end(&SelectToEnd, cx);
 1260        assert_eq!(
 1261            view.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1263        );
 1264    });
 1265}
 1266
 1267// TODO: Re-enable this test
 1268#[cfg(target_os = "macos")]
 1269#[gpui::test]
 1270fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1271    init_test(cx, |_| {});
 1272
 1273    let view = cx.add_window(|cx| {
 1274        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1275        build_editor(buffer.clone(), cx)
 1276    });
 1277
 1278    assert_eq!('ⓐ'.len_utf8(), 3);
 1279    assert_eq!('α'.len_utf8(), 2);
 1280
 1281    _ = view.update(cx, |view, cx| {
 1282        view.fold_ranges(
 1283            vec![
 1284                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1285                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1286                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1287            ],
 1288            true,
 1289            cx,
 1290        );
 1291        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1292
 1293        view.move_right(&MoveRight, cx);
 1294        assert_eq!(
 1295            view.selections.display_ranges(cx),
 1296            &[empty_range(0, "".len())]
 1297        );
 1298        view.move_right(&MoveRight, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(0, "ⓐⓑ".len())]
 1302        );
 1303        view.move_right(&MoveRight, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(0, "ⓐⓑ⋯".len())]
 1307        );
 1308
 1309        view.move_down(&MoveDown, cx);
 1310        assert_eq!(
 1311            view.selections.display_ranges(cx),
 1312            &[empty_range(1, "ab⋯e".len())]
 1313        );
 1314        view.move_left(&MoveLeft, cx);
 1315        assert_eq!(
 1316            view.selections.display_ranges(cx),
 1317            &[empty_range(1, "ab⋯".len())]
 1318        );
 1319        view.move_left(&MoveLeft, cx);
 1320        assert_eq!(
 1321            view.selections.display_ranges(cx),
 1322            &[empty_range(1, "ab".len())]
 1323        );
 1324        view.move_left(&MoveLeft, cx);
 1325        assert_eq!(
 1326            view.selections.display_ranges(cx),
 1327            &[empty_range(1, "a".len())]
 1328        );
 1329
 1330        view.move_down(&MoveDown, cx);
 1331        assert_eq!(
 1332            view.selections.display_ranges(cx),
 1333            &[empty_range(2, "α".len())]
 1334        );
 1335        view.move_right(&MoveRight, cx);
 1336        assert_eq!(
 1337            view.selections.display_ranges(cx),
 1338            &[empty_range(2, "αβ".len())]
 1339        );
 1340        view.move_right(&MoveRight, cx);
 1341        assert_eq!(
 1342            view.selections.display_ranges(cx),
 1343            &[empty_range(2, "αβ⋯".len())]
 1344        );
 1345        view.move_right(&MoveRight, cx);
 1346        assert_eq!(
 1347            view.selections.display_ranges(cx),
 1348            &[empty_range(2, "αβ⋯ε".len())]
 1349        );
 1350
 1351        view.move_up(&MoveUp, cx);
 1352        assert_eq!(
 1353            view.selections.display_ranges(cx),
 1354            &[empty_range(1, "ab⋯e".len())]
 1355        );
 1356        view.move_down(&MoveDown, cx);
 1357        assert_eq!(
 1358            view.selections.display_ranges(cx),
 1359            &[empty_range(2, "αβ⋯ε".len())]
 1360        );
 1361        view.move_up(&MoveUp, cx);
 1362        assert_eq!(
 1363            view.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366
 1367        view.move_up(&MoveUp, cx);
 1368        assert_eq!(
 1369            view.selections.display_ranges(cx),
 1370            &[empty_range(0, "ⓐⓑ".len())]
 1371        );
 1372        view.move_left(&MoveLeft, cx);
 1373        assert_eq!(
 1374            view.selections.display_ranges(cx),
 1375            &[empty_range(0, "".len())]
 1376        );
 1377        view.move_left(&MoveLeft, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[empty_range(0, "".len())]
 1381        );
 1382    });
 1383}
 1384
 1385#[gpui::test]
 1386fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1387    init_test(cx, |_| {});
 1388
 1389    let view = cx.add_window(|cx| {
 1390        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1391        build_editor(buffer.clone(), cx)
 1392    });
 1393    _ = view.update(cx, |view, cx| {
 1394        view.change_selections(None, cx, |s| {
 1395            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1396        });
 1397        view.move_down(&MoveDown, cx);
 1398        assert_eq!(
 1399            view.selections.display_ranges(cx),
 1400            &[empty_range(1, "abcd".len())]
 1401        );
 1402
 1403        view.move_down(&MoveDown, cx);
 1404        assert_eq!(
 1405            view.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβγ".len())]
 1407        );
 1408
 1409        view.move_down(&MoveDown, cx);
 1410        assert_eq!(
 1411            view.selections.display_ranges(cx),
 1412            &[empty_range(3, "abcd".len())]
 1413        );
 1414
 1415        view.move_down(&MoveDown, cx);
 1416        assert_eq!(
 1417            view.selections.display_ranges(cx),
 1418            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1419        );
 1420
 1421        view.move_up(&MoveUp, cx);
 1422        assert_eq!(
 1423            view.selections.display_ranges(cx),
 1424            &[empty_range(3, "abcd".len())]
 1425        );
 1426
 1427        view.move_up(&MoveUp, cx);
 1428        assert_eq!(
 1429            view.selections.display_ranges(cx),
 1430            &[empty_range(2, "αβγ".len())]
 1431        );
 1432    });
 1433}
 1434
 1435#[gpui::test]
 1436fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1437    init_test(cx, |_| {});
 1438    let move_to_beg = MoveToBeginningOfLine {
 1439        stop_at_soft_wraps: true,
 1440    };
 1441
 1442    let move_to_end = MoveToEndOfLine {
 1443        stop_at_soft_wraps: true,
 1444    };
 1445
 1446    let view = cx.add_window(|cx| {
 1447        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1448        build_editor(buffer, cx)
 1449    });
 1450    _ = view.update(cx, |view, cx| {
 1451        view.change_selections(None, cx, |s| {
 1452            s.select_display_ranges([
 1453                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1455            ]);
 1456        });
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.move_to_beginning_of_line(&move_to_beg, cx);
 1461        assert_eq!(
 1462            view.selections.display_ranges(cx),
 1463            &[
 1464                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1465                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1466            ]
 1467        );
 1468    });
 1469
 1470    _ = view.update(cx, |view, cx| {
 1471        view.move_to_beginning_of_line(&move_to_beg, cx);
 1472        assert_eq!(
 1473            view.selections.display_ranges(cx),
 1474            &[
 1475                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1476                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1477            ]
 1478        );
 1479    });
 1480
 1481    _ = view.update(cx, |view, cx| {
 1482        view.move_to_beginning_of_line(&move_to_beg, cx);
 1483        assert_eq!(
 1484            view.selections.display_ranges(cx),
 1485            &[
 1486                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1487                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1488            ]
 1489        );
 1490    });
 1491
 1492    _ = view.update(cx, |view, cx| {
 1493        view.move_to_end_of_line(&move_to_end, cx);
 1494        assert_eq!(
 1495            view.selections.display_ranges(cx),
 1496            &[
 1497                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1498                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1499            ]
 1500        );
 1501    });
 1502
 1503    // Moving to the end of line again is a no-op.
 1504    _ = view.update(cx, |view, cx| {
 1505        view.move_to_end_of_line(&move_to_end, cx);
 1506        assert_eq!(
 1507            view.selections.display_ranges(cx),
 1508            &[
 1509                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1510                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1511            ]
 1512        );
 1513    });
 1514
 1515    _ = view.update(cx, |view, cx| {
 1516        view.move_left(&MoveLeft, cx);
 1517        view.select_to_beginning_of_line(
 1518            &SelectToBeginningOfLine {
 1519                stop_at_soft_wraps: true,
 1520            },
 1521            cx,
 1522        );
 1523        assert_eq!(
 1524            view.selections.display_ranges(cx),
 1525            &[
 1526                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1527                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1528            ]
 1529        );
 1530    });
 1531
 1532    _ = view.update(cx, |view, cx| {
 1533        view.select_to_beginning_of_line(
 1534            &SelectToBeginningOfLine {
 1535                stop_at_soft_wraps: true,
 1536            },
 1537            cx,
 1538        );
 1539        assert_eq!(
 1540            view.selections.display_ranges(cx),
 1541            &[
 1542                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1543                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1544            ]
 1545        );
 1546    });
 1547
 1548    _ = view.update(cx, |view, cx| {
 1549        view.select_to_beginning_of_line(
 1550            &SelectToBeginningOfLine {
 1551                stop_at_soft_wraps: true,
 1552            },
 1553            cx,
 1554        );
 1555        assert_eq!(
 1556            view.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = view.update(cx, |view, cx| {
 1565        view.select_to_end_of_line(
 1566            &SelectToEndOfLine {
 1567                stop_at_soft_wraps: true,
 1568            },
 1569            cx,
 1570        );
 1571        assert_eq!(
 1572            view.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1575                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = view.update(cx, |view, cx| {
 1581        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1582        assert_eq!(view.display_text(cx), "ab\n  de");
 1583        assert_eq!(
 1584            view.selections.display_ranges(cx),
 1585            &[
 1586                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1587                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1588            ]
 1589        );
 1590    });
 1591
 1592    _ = view.update(cx, |view, cx| {
 1593        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1594        assert_eq!(view.display_text(cx), "\n");
 1595        assert_eq!(
 1596            view.selections.display_ranges(cx),
 1597            &[
 1598                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1599                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1600            ]
 1601        );
 1602    });
 1603}
 1604
 1605#[gpui::test]
 1606fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1607    init_test(cx, |_| {});
 1608    let move_to_beg = MoveToBeginningOfLine {
 1609        stop_at_soft_wraps: false,
 1610    };
 1611
 1612    let move_to_end = MoveToEndOfLine {
 1613        stop_at_soft_wraps: false,
 1614    };
 1615
 1616    let view = cx.add_window(|cx| {
 1617        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1618        build_editor(buffer, cx)
 1619    });
 1620
 1621    _ = view.update(cx, |view, cx| {
 1622        view.set_wrap_width(Some(140.0.into()), cx);
 1623
 1624        // We expect the following lines after wrapping
 1625        // ```
 1626        // thequickbrownfox
 1627        // jumpedoverthelazydo
 1628        // gs
 1629        // ```
 1630        // The final `gs` was soft-wrapped onto a new line.
 1631        assert_eq!(
 1632            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1633            view.display_text(cx),
 1634        );
 1635
 1636        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1637        // Start the cursor at the `k` on the first line
 1638        view.change_selections(None, cx, |s| {
 1639            s.select_display_ranges([
 1640                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1641            ]);
 1642        });
 1643
 1644        // Moving to the beginning of the line should put us at the beginning of the line.
 1645        view.move_to_beginning_of_line(&move_to_beg, cx);
 1646        assert_eq!(
 1647            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1648            view.selections.display_ranges(cx)
 1649        );
 1650
 1651        // Moving to the end of the line should put us at the end of the line.
 1652        view.move_to_end_of_line(&move_to_end, cx);
 1653        assert_eq!(
 1654            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1655            view.selections.display_ranges(cx)
 1656        );
 1657
 1658        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1659        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1660        view.change_selections(None, cx, |s| {
 1661            s.select_display_ranges([
 1662                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1663            ]);
 1664        });
 1665
 1666        // Moving to the beginning of the line should put us at the start of the second line of
 1667        // display text, i.e., the `j`.
 1668        view.move_to_beginning_of_line(&move_to_beg, cx);
 1669        assert_eq!(
 1670            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1671            view.selections.display_ranges(cx)
 1672        );
 1673
 1674        // Moving to the beginning of the line again should be a no-op.
 1675        view.move_to_beginning_of_line(&move_to_beg, cx);
 1676        assert_eq!(
 1677            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1678            view.selections.display_ranges(cx)
 1679        );
 1680
 1681        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1682        // next display line.
 1683        view.move_to_end_of_line(&move_to_end, cx);
 1684        assert_eq!(
 1685            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1686            view.selections.display_ranges(cx)
 1687        );
 1688
 1689        // Moving to the end of the line again should be a no-op.
 1690        view.move_to_end_of_line(&move_to_end, cx);
 1691        assert_eq!(
 1692            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1693            view.selections.display_ranges(cx)
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701
 1702    let view = cx.add_window(|cx| {
 1703        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1704        build_editor(buffer, cx)
 1705    });
 1706    _ = view.update(cx, |view, cx| {
 1707        view.change_selections(None, cx, |s| {
 1708            s.select_display_ranges([
 1709                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1710                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1711            ])
 1712        });
 1713
 1714        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1715        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1716
 1717        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1718        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1719
 1720        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1721        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1722
 1723        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1724        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1725
 1726        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1727        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1728
 1729        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1730        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1731
 1732        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1733        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1734
 1735        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1736        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1737
 1738        view.move_right(&MoveRight, cx);
 1739        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1740        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1741
 1742        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1743        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1744
 1745        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1746        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1747    });
 1748}
 1749
 1750#[gpui::test]
 1751fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1752    init_test(cx, |_| {});
 1753
 1754    let view = cx.add_window(|cx| {
 1755        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1756        build_editor(buffer, cx)
 1757    });
 1758
 1759    _ = view.update(cx, |view, cx| {
 1760        view.set_wrap_width(Some(140.0.into()), cx);
 1761        assert_eq!(
 1762            view.display_text(cx),
 1763            "use one::{\n    two::three::\n    four::five\n};"
 1764        );
 1765
 1766        view.change_selections(None, cx, |s| {
 1767            s.select_display_ranges([
 1768                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1769            ]);
 1770        });
 1771
 1772        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1773        assert_eq!(
 1774            view.selections.display_ranges(cx),
 1775            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1776        );
 1777
 1778        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1779        assert_eq!(
 1780            view.selections.display_ranges(cx),
 1781            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1782        );
 1783
 1784        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1785        assert_eq!(
 1786            view.selections.display_ranges(cx),
 1787            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1788        );
 1789
 1790        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1791        assert_eq!(
 1792            view.selections.display_ranges(cx),
 1793            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1794        );
 1795
 1796        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1797        assert_eq!(
 1798            view.selections.display_ranges(cx),
 1799            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1800        );
 1801
 1802        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1803        assert_eq!(
 1804            view.selections.display_ranges(cx),
 1805            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1806        );
 1807    });
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814
 1815    let line_height = cx.editor(|editor, cx| {
 1816        editor
 1817            .style()
 1818            .unwrap()
 1819            .text
 1820            .line_height_in_pixels(cx.rem_size())
 1821    });
 1822    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827
 1828        three
 1829        fourˇ
 1830        five
 1831
 1832        six"#
 1833            .unindent(),
 1834    );
 1835
 1836    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1837    cx.assert_editor_state(
 1838        &r#"one
 1839        two
 1840        ˇ
 1841        three
 1842        four
 1843        five
 1844        ˇ
 1845        six"#
 1846            .unindent(),
 1847    );
 1848
 1849    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1850    cx.assert_editor_state(
 1851        &r#"one
 1852        two
 1853
 1854        three
 1855        four
 1856        five
 1857        ˇ
 1858        sixˇ"#
 1859            .unindent(),
 1860    );
 1861
 1862    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1863    cx.assert_editor_state(
 1864        &r#"one
 1865        two
 1866
 1867        three
 1868        four
 1869        five
 1870
 1871        sixˇ"#
 1872            .unindent(),
 1873    );
 1874
 1875    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1876    cx.assert_editor_state(
 1877        &r#"one
 1878        two
 1879
 1880        three
 1881        four
 1882        five
 1883        ˇ
 1884        six"#
 1885            .unindent(),
 1886    );
 1887
 1888    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1889    cx.assert_editor_state(
 1890        &r#"one
 1891        two
 1892        ˇ
 1893        three
 1894        four
 1895        five
 1896
 1897        six"#
 1898            .unindent(),
 1899    );
 1900
 1901    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1902    cx.assert_editor_state(
 1903        &r#"ˇone
 1904        two
 1905
 1906        three
 1907        four
 1908        five
 1909
 1910        six"#
 1911            .unindent(),
 1912    );
 1913}
 1914
 1915#[gpui::test]
 1916async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1917    init_test(cx, |_| {});
 1918    let mut cx = EditorTestContext::new(cx).await;
 1919    let line_height = cx.editor(|editor, cx| {
 1920        editor
 1921            .style()
 1922            .unwrap()
 1923            .text
 1924            .line_height_in_pixels(cx.rem_size())
 1925    });
 1926    let window = cx.window;
 1927    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1928
 1929    cx.set_state(
 1930        r#"ˇone
 1931        two
 1932        three
 1933        four
 1934        five
 1935        six
 1936        seven
 1937        eight
 1938        nine
 1939        ten
 1940        "#,
 1941    );
 1942
 1943    cx.update_editor(|editor, cx| {
 1944        assert_eq!(
 1945            editor.snapshot(cx).scroll_position(),
 1946            gpui::Point::new(0., 0.)
 1947        );
 1948        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1949        assert_eq!(
 1950            editor.snapshot(cx).scroll_position(),
 1951            gpui::Point::new(0., 3.)
 1952        );
 1953        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1954        assert_eq!(
 1955            editor.snapshot(cx).scroll_position(),
 1956            gpui::Point::new(0., 6.)
 1957        );
 1958        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1959        assert_eq!(
 1960            editor.snapshot(cx).scroll_position(),
 1961            gpui::Point::new(0., 3.)
 1962        );
 1963
 1964        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1965        assert_eq!(
 1966            editor.snapshot(cx).scroll_position(),
 1967            gpui::Point::new(0., 1.)
 1968        );
 1969        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1970        assert_eq!(
 1971            editor.snapshot(cx).scroll_position(),
 1972            gpui::Point::new(0., 3.)
 1973        );
 1974    });
 1975}
 1976
 1977#[gpui::test]
 1978async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1979    init_test(cx, |_| {});
 1980    let mut cx = EditorTestContext::new(cx).await;
 1981
 1982    let line_height = cx.update_editor(|editor, cx| {
 1983        editor.set_vertical_scroll_margin(2, cx);
 1984        editor
 1985            .style()
 1986            .unwrap()
 1987            .text
 1988            .line_height_in_pixels(cx.rem_size())
 1989    });
 1990    let window = cx.window;
 1991    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1992
 1993    cx.set_state(
 1994        r#"ˇone
 1995            two
 1996            three
 1997            four
 1998            five
 1999            six
 2000            seven
 2001            eight
 2002            nine
 2003            ten
 2004        "#,
 2005    );
 2006    cx.update_editor(|editor, cx| {
 2007        assert_eq!(
 2008            editor.snapshot(cx).scroll_position(),
 2009            gpui::Point::new(0., 0.0)
 2010        );
 2011    });
 2012
 2013    // Add a cursor below the visible area. Since both cursors cannot fit
 2014    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2015    // allows the vertical scroll margin below that cursor.
 2016    cx.update_editor(|editor, cx| {
 2017        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2018            selections.select_ranges([
 2019                Point::new(0, 0)..Point::new(0, 0),
 2020                Point::new(6, 0)..Point::new(6, 0),
 2021            ]);
 2022        })
 2023    });
 2024    cx.update_editor(|editor, cx| {
 2025        assert_eq!(
 2026            editor.snapshot(cx).scroll_position(),
 2027            gpui::Point::new(0., 3.0)
 2028        );
 2029    });
 2030
 2031    // Move down. The editor cursor scrolls down to track the newest cursor.
 2032    cx.update_editor(|editor, cx| {
 2033        editor.move_down(&Default::default(), cx);
 2034    });
 2035    cx.update_editor(|editor, cx| {
 2036        assert_eq!(
 2037            editor.snapshot(cx).scroll_position(),
 2038            gpui::Point::new(0., 4.0)
 2039        );
 2040    });
 2041
 2042    // Add a cursor above the visible area. Since both cursors fit on screen,
 2043    // the editor scrolls to show both.
 2044    cx.update_editor(|editor, cx| {
 2045        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2046            selections.select_ranges([
 2047                Point::new(1, 0)..Point::new(1, 0),
 2048                Point::new(6, 0)..Point::new(6, 0),
 2049            ]);
 2050        })
 2051    });
 2052    cx.update_editor(|editor, cx| {
 2053        assert_eq!(
 2054            editor.snapshot(cx).scroll_position(),
 2055            gpui::Point::new(0., 1.0)
 2056        );
 2057    });
 2058}
 2059
 2060#[gpui::test]
 2061async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2062    init_test(cx, |_| {});
 2063    let mut cx = EditorTestContext::new(cx).await;
 2064
 2065    let line_height = cx.editor(|editor, cx| {
 2066        editor
 2067            .style()
 2068            .unwrap()
 2069            .text
 2070            .line_height_in_pixels(cx.rem_size())
 2071    });
 2072    let window = cx.window;
 2073    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2074    cx.set_state(
 2075        &r#"
 2076        ˇone
 2077        two
 2078        threeˇ
 2079        four
 2080        five
 2081        six
 2082        seven
 2083        eight
 2084        nine
 2085        ten
 2086        "#
 2087        .unindent(),
 2088    );
 2089
 2090    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2091    cx.assert_editor_state(
 2092        &r#"
 2093        one
 2094        two
 2095        three
 2096        ˇfour
 2097        five
 2098        sixˇ
 2099        seven
 2100        eight
 2101        nine
 2102        ten
 2103        "#
 2104        .unindent(),
 2105    );
 2106
 2107    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2108    cx.assert_editor_state(
 2109        &r#"
 2110        one
 2111        two
 2112        three
 2113        four
 2114        five
 2115        six
 2116        ˇseven
 2117        eight
 2118        nineˇ
 2119        ten
 2120        "#
 2121        .unindent(),
 2122    );
 2123
 2124    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2125    cx.assert_editor_state(
 2126        &r#"
 2127        one
 2128        two
 2129        three
 2130        ˇfour
 2131        five
 2132        sixˇ
 2133        seven
 2134        eight
 2135        nine
 2136        ten
 2137        "#
 2138        .unindent(),
 2139    );
 2140
 2141    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2142    cx.assert_editor_state(
 2143        &r#"
 2144        ˇone
 2145        two
 2146        threeˇ
 2147        four
 2148        five
 2149        six
 2150        seven
 2151        eight
 2152        nine
 2153        ten
 2154        "#
 2155        .unindent(),
 2156    );
 2157
 2158    // Test select collapsing
 2159    cx.update_editor(|editor, cx| {
 2160        editor.move_page_down(&MovePageDown::default(), cx);
 2161        editor.move_page_down(&MovePageDown::default(), cx);
 2162        editor.move_page_down(&MovePageDown::default(), cx);
 2163    });
 2164    cx.assert_editor_state(
 2165        &r#"
 2166        one
 2167        two
 2168        three
 2169        four
 2170        five
 2171        six
 2172        seven
 2173        eight
 2174        nine
 2175        ˇten
 2176        ˇ"#
 2177        .unindent(),
 2178    );
 2179}
 2180
 2181#[gpui::test]
 2182async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2183    init_test(cx, |_| {});
 2184    let mut cx = EditorTestContext::new(cx).await;
 2185    cx.set_state("one «two threeˇ» four");
 2186    cx.update_editor(|editor, cx| {
 2187        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2188        assert_eq!(editor.text(cx), " four");
 2189    });
 2190}
 2191
 2192#[gpui::test]
 2193fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2194    init_test(cx, |_| {});
 2195
 2196    let view = cx.add_window(|cx| {
 2197        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2198        build_editor(buffer.clone(), cx)
 2199    });
 2200
 2201    _ = view.update(cx, |view, cx| {
 2202        view.change_selections(None, cx, |s| {
 2203            s.select_display_ranges([
 2204                // an empty selection - the preceding word fragment is deleted
 2205                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2206                // characters selected - they are deleted
 2207                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2208            ])
 2209        });
 2210        view.delete_to_previous_word_start(
 2211            &DeleteToPreviousWordStart {
 2212                ignore_newlines: false,
 2213            },
 2214            cx,
 2215        );
 2216        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2217    });
 2218
 2219    _ = view.update(cx, |view, cx| {
 2220        view.change_selections(None, cx, |s| {
 2221            s.select_display_ranges([
 2222                // an empty selection - the following word fragment is deleted
 2223                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2224                // characters selected - they are deleted
 2225                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2226            ])
 2227        });
 2228        view.delete_to_next_word_end(
 2229            &DeleteToNextWordEnd {
 2230                ignore_newlines: false,
 2231            },
 2232            cx,
 2233        );
 2234        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2235    });
 2236}
 2237
 2238#[gpui::test]
 2239fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2240    init_test(cx, |_| {});
 2241
 2242    let view = cx.add_window(|cx| {
 2243        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2244        build_editor(buffer.clone(), cx)
 2245    });
 2246    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2247        ignore_newlines: false,
 2248    };
 2249    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2250        ignore_newlines: true,
 2251    };
 2252
 2253    _ = view.update(cx, |view, cx| {
 2254        view.change_selections(None, cx, |s| {
 2255            s.select_display_ranges([
 2256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2257            ])
 2258        });
 2259        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2260        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2261        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2262        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2263        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2264        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2265        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2266        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2267        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2268        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2269        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_next_word_end = DeleteToNextWordEnd {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 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(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2293            ])
 2294        });
 2295        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2296        assert_eq!(
 2297            view.buffer.read(cx).read(cx).text(),
 2298            "one\n   two\nthree\n   four"
 2299        );
 2300        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2301        assert_eq!(
 2302            view.buffer.read(cx).read(cx).text(),
 2303            "\n   two\nthree\n   four"
 2304        );
 2305        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2307        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2308        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2309        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2310        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2311        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2312        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317fn test_newline(cx: &mut TestAppContext) {
 2318    init_test(cx, |_| {});
 2319
 2320    let view = cx.add_window(|cx| {
 2321        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2322        build_editor(buffer.clone(), cx)
 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), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2329                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2330                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2331            ])
 2332        });
 2333
 2334        view.newline(&Newline, cx);
 2335        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2336    });
 2337}
 2338
 2339#[gpui::test]
 2340fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2341    init_test(cx, |_| {});
 2342
 2343    let editor = cx.add_window(|cx| {
 2344        let buffer = MultiBuffer::build_simple(
 2345            "
 2346                a
 2347                b(
 2348                    X
 2349                )
 2350                c(
 2351                    X
 2352                )
 2353            "
 2354            .unindent()
 2355            .as_str(),
 2356            cx,
 2357        );
 2358        let mut editor = build_editor(buffer.clone(), cx);
 2359        editor.change_selections(None, cx, |s| {
 2360            s.select_ranges([
 2361                Point::new(2, 4)..Point::new(2, 5),
 2362                Point::new(5, 4)..Point::new(5, 5),
 2363            ])
 2364        });
 2365        editor
 2366    });
 2367
 2368    _ = editor.update(cx, |editor, cx| {
 2369        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2370        editor.buffer.update(cx, |buffer, cx| {
 2371            buffer.edit(
 2372                [
 2373                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2374                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2375                ],
 2376                None,
 2377                cx,
 2378            );
 2379            assert_eq!(
 2380                buffer.read(cx).text(),
 2381                "
 2382                    a
 2383                    b()
 2384                    c()
 2385                "
 2386                .unindent()
 2387            );
 2388        });
 2389        assert_eq!(
 2390            editor.selections.ranges(cx),
 2391            &[
 2392                Point::new(1, 2)..Point::new(1, 2),
 2393                Point::new(2, 2)..Point::new(2, 2),
 2394            ],
 2395        );
 2396
 2397        editor.newline(&Newline, cx);
 2398        assert_eq!(
 2399            editor.text(cx),
 2400            "
 2401                a
 2402                b(
 2403                )
 2404                c(
 2405                )
 2406            "
 2407            .unindent()
 2408        );
 2409
 2410        // The selections are moved after the inserted newlines
 2411        assert_eq!(
 2412            editor.selections.ranges(cx),
 2413            &[
 2414                Point::new(2, 0)..Point::new(2, 0),
 2415                Point::new(4, 0)..Point::new(4, 0),
 2416            ],
 2417        );
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2423    init_test(cx, |settings| {
 2424        settings.defaults.tab_size = NonZeroU32::new(4)
 2425    });
 2426
 2427    let language = Arc::new(
 2428        Language::new(
 2429            LanguageConfig::default(),
 2430            Some(tree_sitter_rust::LANGUAGE.into()),
 2431        )
 2432        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2433        .unwrap(),
 2434    );
 2435
 2436    let mut cx = EditorTestContext::new(cx).await;
 2437    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2438    cx.set_state(indoc! {"
 2439        const a: ˇA = (
 2440 2441                «const_functionˇ»(ˇ),
 2442                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2443 2444        ˇ);ˇ
 2445    "});
 2446
 2447    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2448    cx.assert_editor_state(indoc! {"
 2449        ˇ
 2450        const a: A = (
 2451            ˇ
 2452            (
 2453                ˇ
 2454                ˇ
 2455                const_function(),
 2456                ˇ
 2457                ˇ
 2458                ˇ
 2459                ˇ
 2460                something_else,
 2461                ˇ
 2462            )
 2463            ˇ
 2464            ˇ
 2465        );
 2466    "});
 2467}
 2468
 2469#[gpui::test]
 2470async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2471    init_test(cx, |settings| {
 2472        settings.defaults.tab_size = NonZeroU32::new(4)
 2473    });
 2474
 2475    let language = Arc::new(
 2476        Language::new(
 2477            LanguageConfig::default(),
 2478            Some(tree_sitter_rust::LANGUAGE.into()),
 2479        )
 2480        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2481        .unwrap(),
 2482    );
 2483
 2484    let mut cx = EditorTestContext::new(cx).await;
 2485    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2486    cx.set_state(indoc! {"
 2487        const a: ˇA = (
 2488 2489                «const_functionˇ»(ˇ),
 2490                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2491 2492        ˇ);ˇ
 2493    "});
 2494
 2495    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2496    cx.assert_editor_state(indoc! {"
 2497        const a: A = (
 2498            ˇ
 2499            (
 2500                ˇ
 2501                const_function(),
 2502                ˇ
 2503                ˇ
 2504                something_else,
 2505                ˇ
 2506                ˇ
 2507                ˇ
 2508                ˇ
 2509            )
 2510            ˇ
 2511        );
 2512        ˇ
 2513        ˇ
 2514    "});
 2515}
 2516
 2517#[gpui::test]
 2518async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2519    init_test(cx, |settings| {
 2520        settings.defaults.tab_size = NonZeroU32::new(4)
 2521    });
 2522
 2523    let language = Arc::new(Language::new(
 2524        LanguageConfig {
 2525            line_comments: vec!["//".into()],
 2526            ..LanguageConfig::default()
 2527        },
 2528        None,
 2529    ));
 2530    {
 2531        let mut cx = EditorTestContext::new(cx).await;
 2532        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533        cx.set_state(indoc! {"
 2534        // Fooˇ
 2535    "});
 2536
 2537        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2538        cx.assert_editor_state(indoc! {"
 2539        // Foo
 2540        //ˇ
 2541    "});
 2542        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2543        cx.set_state(indoc! {"
 2544        ˇ// Foo
 2545    "});
 2546        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2547        cx.assert_editor_state(indoc! {"
 2548
 2549        ˇ// Foo
 2550    "});
 2551    }
 2552    // Ensure that comment continuations can be disabled.
 2553    update_test_language_settings(cx, |settings| {
 2554        settings.defaults.extend_comment_on_newline = Some(false);
 2555    });
 2556    let mut cx = EditorTestContext::new(cx).await;
 2557    cx.set_state(indoc! {"
 2558        // Fooˇ
 2559    "});
 2560    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2561    cx.assert_editor_state(indoc! {"
 2562        // Foo
 2563        ˇ
 2564    "});
 2565}
 2566
 2567#[gpui::test]
 2568fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2569    init_test(cx, |_| {});
 2570
 2571    let editor = cx.add_window(|cx| {
 2572        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2573        let mut editor = build_editor(buffer.clone(), cx);
 2574        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2575        editor
 2576    });
 2577
 2578    _ = editor.update(cx, |editor, cx| {
 2579        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2580        editor.buffer.update(cx, |buffer, cx| {
 2581            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2582            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2583        });
 2584        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2585
 2586        editor.insert("Z", cx);
 2587        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2588
 2589        // The selections are moved after the inserted characters
 2590        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2591    });
 2592}
 2593
 2594#[gpui::test]
 2595async fn test_tab(cx: &mut gpui::TestAppContext) {
 2596    init_test(cx, |settings| {
 2597        settings.defaults.tab_size = NonZeroU32::new(3)
 2598    });
 2599
 2600    let mut cx = EditorTestContext::new(cx).await;
 2601    cx.set_state(indoc! {"
 2602        ˇabˇc
 2603        ˇ🏀ˇ🏀ˇefg
 2604 2605    "});
 2606    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2607    cx.assert_editor_state(indoc! {"
 2608           ˇab ˇc
 2609           ˇ🏀  ˇ🏀  ˇefg
 2610        d  ˇ
 2611    "});
 2612
 2613    cx.set_state(indoc! {"
 2614        a
 2615        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2616    "});
 2617    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2618    cx.assert_editor_state(indoc! {"
 2619        a
 2620           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2621    "});
 2622}
 2623
 2624#[gpui::test]
 2625async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2626    init_test(cx, |_| {});
 2627
 2628    let mut cx = EditorTestContext::new(cx).await;
 2629    let language = Arc::new(
 2630        Language::new(
 2631            LanguageConfig::default(),
 2632            Some(tree_sitter_rust::LANGUAGE.into()),
 2633        )
 2634        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2635        .unwrap(),
 2636    );
 2637    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2638
 2639    // cursors that are already at the suggested indent level insert
 2640    // a soft tab. cursors that are to the left of the suggested indent
 2641    // auto-indent their line.
 2642    cx.set_state(indoc! {"
 2643        ˇ
 2644        const a: B = (
 2645            c(
 2646                d(
 2647        ˇ
 2648                )
 2649        ˇ
 2650        ˇ    )
 2651        );
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655            ˇ
 2656        const a: B = (
 2657            c(
 2658                d(
 2659                    ˇ
 2660                )
 2661                ˇ
 2662            ˇ)
 2663        );
 2664    "});
 2665
 2666    // handle auto-indent when there are multiple cursors on the same line
 2667    cx.set_state(indoc! {"
 2668        const a: B = (
 2669            c(
 2670        ˇ    ˇ
 2671        ˇ    )
 2672        );
 2673    "});
 2674    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2675    cx.assert_editor_state(indoc! {"
 2676        const a: B = (
 2677            c(
 2678                ˇ
 2679            ˇ)
 2680        );
 2681    "});
 2682}
 2683
 2684#[gpui::test]
 2685async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2686    init_test(cx, |settings| {
 2687        settings.defaults.tab_size = NonZeroU32::new(4)
 2688    });
 2689
 2690    let language = Arc::new(
 2691        Language::new(
 2692            LanguageConfig::default(),
 2693            Some(tree_sitter_rust::LANGUAGE.into()),
 2694        )
 2695        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2696        .unwrap(),
 2697    );
 2698
 2699    let mut cx = EditorTestContext::new(cx).await;
 2700    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2701    cx.set_state(indoc! {"
 2702        fn a() {
 2703            if b {
 2704        \t ˇc
 2705            }
 2706        }
 2707    "});
 2708
 2709    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2710    cx.assert_editor_state(indoc! {"
 2711        fn a() {
 2712            if b {
 2713                ˇc
 2714            }
 2715        }
 2716    "});
 2717}
 2718
 2719#[gpui::test]
 2720async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2721    init_test(cx, |settings| {
 2722        settings.defaults.tab_size = NonZeroU32::new(4);
 2723    });
 2724
 2725    let mut cx = EditorTestContext::new(cx).await;
 2726
 2727    cx.set_state(indoc! {"
 2728          «oneˇ» «twoˇ»
 2729        three
 2730         four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734            «oneˇ» «twoˇ»
 2735        three
 2736         four
 2737    "});
 2738
 2739    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2740    cx.assert_editor_state(indoc! {"
 2741        «oneˇ» «twoˇ»
 2742        three
 2743         four
 2744    "});
 2745
 2746    // select across line ending
 2747    cx.set_state(indoc! {"
 2748        one two
 2749        t«hree
 2750        ˇ» four
 2751    "});
 2752    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2753    cx.assert_editor_state(indoc! {"
 2754        one two
 2755            t«hree
 2756        ˇ» four
 2757    "});
 2758
 2759    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2760    cx.assert_editor_state(indoc! {"
 2761        one two
 2762        t«hree
 2763        ˇ» four
 2764    "});
 2765
 2766    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2767    cx.set_state(indoc! {"
 2768        one two
 2769        ˇthree
 2770            four
 2771    "});
 2772    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2773    cx.assert_editor_state(indoc! {"
 2774        one two
 2775            ˇthree
 2776            four
 2777    "});
 2778
 2779    cx.set_state(indoc! {"
 2780        one two
 2781        ˇ    three
 2782            four
 2783    "});
 2784    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2785    cx.assert_editor_state(indoc! {"
 2786        one two
 2787        ˇthree
 2788            four
 2789    "});
 2790}
 2791
 2792#[gpui::test]
 2793async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2794    init_test(cx, |settings| {
 2795        settings.defaults.hard_tabs = Some(true);
 2796    });
 2797
 2798    let mut cx = EditorTestContext::new(cx).await;
 2799
 2800    // select two ranges on one line
 2801    cx.set_state(indoc! {"
 2802        «oneˇ» «twoˇ»
 2803        three
 2804        four
 2805    "});
 2806    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2807    cx.assert_editor_state(indoc! {"
 2808        \t«oneˇ» «twoˇ»
 2809        three
 2810        four
 2811    "});
 2812    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2813    cx.assert_editor_state(indoc! {"
 2814        \t\t«oneˇ» «twoˇ»
 2815        three
 2816        four
 2817    "});
 2818    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2819    cx.assert_editor_state(indoc! {"
 2820        \t«oneˇ» «twoˇ»
 2821        three
 2822        four
 2823    "});
 2824    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2825    cx.assert_editor_state(indoc! {"
 2826        «oneˇ» «twoˇ»
 2827        three
 2828        four
 2829    "});
 2830
 2831    // select across a line ending
 2832    cx.set_state(indoc! {"
 2833        one two
 2834        t«hree
 2835        ˇ»four
 2836    "});
 2837    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2838    cx.assert_editor_state(indoc! {"
 2839        one two
 2840        \tt«hree
 2841        ˇ»four
 2842    "});
 2843    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2844    cx.assert_editor_state(indoc! {"
 2845        one two
 2846        \t\tt«hree
 2847        ˇ»four
 2848    "});
 2849    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2850    cx.assert_editor_state(indoc! {"
 2851        one two
 2852        \tt«hree
 2853        ˇ»four
 2854    "});
 2855    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2856    cx.assert_editor_state(indoc! {"
 2857        one two
 2858        t«hree
 2859        ˇ»four
 2860    "});
 2861
 2862    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2863    cx.set_state(indoc! {"
 2864        one two
 2865        ˇthree
 2866        four
 2867    "});
 2868    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        one two
 2871        ˇthree
 2872        four
 2873    "});
 2874    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2875    cx.assert_editor_state(indoc! {"
 2876        one two
 2877        \tˇthree
 2878        four
 2879    "});
 2880    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2881    cx.assert_editor_state(indoc! {"
 2882        one two
 2883        ˇthree
 2884        four
 2885    "});
 2886}
 2887
 2888#[gpui::test]
 2889fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2890    init_test(cx, |settings| {
 2891        settings.languages.extend([
 2892            (
 2893                "TOML".into(),
 2894                LanguageSettingsContent {
 2895                    tab_size: NonZeroU32::new(2),
 2896                    ..Default::default()
 2897                },
 2898            ),
 2899            (
 2900                "Rust".into(),
 2901                LanguageSettingsContent {
 2902                    tab_size: NonZeroU32::new(4),
 2903                    ..Default::default()
 2904                },
 2905            ),
 2906        ]);
 2907    });
 2908
 2909    let toml_language = Arc::new(Language::new(
 2910        LanguageConfig {
 2911            name: "TOML".into(),
 2912            ..Default::default()
 2913        },
 2914        None,
 2915    ));
 2916    let rust_language = Arc::new(Language::new(
 2917        LanguageConfig {
 2918            name: "Rust".into(),
 2919            ..Default::default()
 2920        },
 2921        None,
 2922    ));
 2923
 2924    let toml_buffer =
 2925        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2926    let rust_buffer = cx.new_model(|cx| {
 2927        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2928    });
 2929    let multibuffer = cx.new_model(|cx| {
 2930        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2931        multibuffer.push_excerpts(
 2932            toml_buffer.clone(),
 2933            [ExcerptRange {
 2934                context: Point::new(0, 0)..Point::new(2, 0),
 2935                primary: None,
 2936            }],
 2937            cx,
 2938        );
 2939        multibuffer.push_excerpts(
 2940            rust_buffer.clone(),
 2941            [ExcerptRange {
 2942                context: Point::new(0, 0)..Point::new(1, 0),
 2943                primary: None,
 2944            }],
 2945            cx,
 2946        );
 2947        multibuffer
 2948    });
 2949
 2950    cx.add_window(|cx| {
 2951        let mut editor = build_editor(multibuffer, cx);
 2952
 2953        assert_eq!(
 2954            editor.text(cx),
 2955            indoc! {"
 2956                a = 1
 2957                b = 2
 2958
 2959                const c: usize = 3;
 2960            "}
 2961        );
 2962
 2963        select_ranges(
 2964            &mut editor,
 2965            indoc! {"
 2966                «aˇ» = 1
 2967                b = 2
 2968
 2969                «const c:ˇ» usize = 3;
 2970            "},
 2971            cx,
 2972        );
 2973
 2974        editor.tab(&Tab, cx);
 2975        assert_text_with_selections(
 2976            &mut editor,
 2977            indoc! {"
 2978                  «aˇ» = 1
 2979                b = 2
 2980
 2981                    «const c:ˇ» usize = 3;
 2982            "},
 2983            cx,
 2984        );
 2985        editor.tab_prev(&TabPrev, cx);
 2986        assert_text_with_selections(
 2987            &mut editor,
 2988            indoc! {"
 2989                «aˇ» = 1
 2990                b = 2
 2991
 2992                «const c:ˇ» usize = 3;
 2993            "},
 2994            cx,
 2995        );
 2996
 2997        editor
 2998    });
 2999}
 3000
 3001#[gpui::test]
 3002async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3003    init_test(cx, |_| {});
 3004
 3005    let mut cx = EditorTestContext::new(cx).await;
 3006
 3007    // Basic backspace
 3008    cx.set_state(indoc! {"
 3009        onˇe two three
 3010        fou«rˇ» five six
 3011        seven «ˇeight nine
 3012        »ten
 3013    "});
 3014    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3015    cx.assert_editor_state(indoc! {"
 3016        oˇe two three
 3017        fouˇ five six
 3018        seven ˇten
 3019    "});
 3020
 3021    // Test backspace inside and around indents
 3022    cx.set_state(indoc! {"
 3023        zero
 3024            ˇone
 3025                ˇtwo
 3026            ˇ ˇ ˇ  three
 3027        ˇ  ˇ  four
 3028    "});
 3029    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3030    cx.assert_editor_state(indoc! {"
 3031        zero
 3032        ˇone
 3033            ˇtwo
 3034        ˇ  threeˇ  four
 3035    "});
 3036
 3037    // Test backspace with line_mode set to true
 3038    cx.update_editor(|e, _| e.selections.line_mode = true);
 3039    cx.set_state(indoc! {"
 3040        The ˇquick ˇbrown
 3041        fox jumps over
 3042        the lazy dog
 3043        ˇThe qu«ick bˇ»rown"});
 3044    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3045    cx.assert_editor_state(indoc! {"
 3046        ˇfox jumps over
 3047        the lazy dogˇ"});
 3048}
 3049
 3050#[gpui::test]
 3051async fn test_delete(cx: &mut gpui::TestAppContext) {
 3052    init_test(cx, |_| {});
 3053
 3054    let mut cx = EditorTestContext::new(cx).await;
 3055    cx.set_state(indoc! {"
 3056        onˇe two three
 3057        fou«rˇ» five six
 3058        seven «ˇeight nine
 3059        »ten
 3060    "});
 3061    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3062    cx.assert_editor_state(indoc! {"
 3063        onˇ two three
 3064        fouˇ five six
 3065        seven ˇten
 3066    "});
 3067
 3068    // Test backspace with line_mode set to true
 3069    cx.update_editor(|e, _| e.selections.line_mode = true);
 3070    cx.set_state(indoc! {"
 3071        The ˇquick ˇbrown
 3072        fox «ˇjum»ps over
 3073        the lazy dog
 3074        ˇThe qu«ick bˇ»rown"});
 3075    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3076    cx.assert_editor_state("ˇthe lazy dogˇ");
 3077}
 3078
 3079#[gpui::test]
 3080fn test_delete_line(cx: &mut TestAppContext) {
 3081    init_test(cx, |_| {});
 3082
 3083    let view = cx.add_window(|cx| {
 3084        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3085        build_editor(buffer, cx)
 3086    });
 3087    _ = view.update(cx, |view, cx| {
 3088        view.change_selections(None, cx, |s| {
 3089            s.select_display_ranges([
 3090                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3091                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3092                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3093            ])
 3094        });
 3095        view.delete_line(&DeleteLine, cx);
 3096        assert_eq!(view.display_text(cx), "ghi");
 3097        assert_eq!(
 3098            view.selections.display_ranges(cx),
 3099            vec![
 3100                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3101                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3102            ]
 3103        );
 3104    });
 3105
 3106    let view = cx.add_window(|cx| {
 3107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3108        build_editor(buffer, cx)
 3109    });
 3110    _ = view.update(cx, |view, cx| {
 3111        view.change_selections(None, cx, |s| {
 3112            s.select_display_ranges([
 3113                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3114            ])
 3115        });
 3116        view.delete_line(&DeleteLine, cx);
 3117        assert_eq!(view.display_text(cx), "ghi\n");
 3118        assert_eq!(
 3119            view.selections.display_ranges(cx),
 3120            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3121        );
 3122    });
 3123}
 3124
 3125#[gpui::test]
 3126fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3127    init_test(cx, |_| {});
 3128
 3129    cx.add_window(|cx| {
 3130        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3131        let mut editor = build_editor(buffer.clone(), cx);
 3132        let buffer = buffer.read(cx).as_singleton().unwrap();
 3133
 3134        assert_eq!(
 3135            editor.selections.ranges::<Point>(cx),
 3136            &[Point::new(0, 0)..Point::new(0, 0)]
 3137        );
 3138
 3139        // When on single line, replace newline at end by space
 3140        editor.join_lines(&JoinLines, cx);
 3141        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3142        assert_eq!(
 3143            editor.selections.ranges::<Point>(cx),
 3144            &[Point::new(0, 3)..Point::new(0, 3)]
 3145        );
 3146
 3147        // When multiple lines are selected, remove newlines that are spanned by the selection
 3148        editor.change_selections(None, cx, |s| {
 3149            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3150        });
 3151        editor.join_lines(&JoinLines, cx);
 3152        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3153        assert_eq!(
 3154            editor.selections.ranges::<Point>(cx),
 3155            &[Point::new(0, 11)..Point::new(0, 11)]
 3156        );
 3157
 3158        // Undo should be transactional
 3159        editor.undo(&Undo, cx);
 3160        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3161        assert_eq!(
 3162            editor.selections.ranges::<Point>(cx),
 3163            &[Point::new(0, 5)..Point::new(2, 2)]
 3164        );
 3165
 3166        // When joining an empty line don't insert a space
 3167        editor.change_selections(None, cx, |s| {
 3168            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3169        });
 3170        editor.join_lines(&JoinLines, cx);
 3171        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3172        assert_eq!(
 3173            editor.selections.ranges::<Point>(cx),
 3174            [Point::new(2, 3)..Point::new(2, 3)]
 3175        );
 3176
 3177        // We can remove trailing newlines
 3178        editor.join_lines(&JoinLines, cx);
 3179        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3180        assert_eq!(
 3181            editor.selections.ranges::<Point>(cx),
 3182            [Point::new(2, 3)..Point::new(2, 3)]
 3183        );
 3184
 3185        // We don't blow up on the last line
 3186        editor.join_lines(&JoinLines, cx);
 3187        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3188        assert_eq!(
 3189            editor.selections.ranges::<Point>(cx),
 3190            [Point::new(2, 3)..Point::new(2, 3)]
 3191        );
 3192
 3193        // reset to test indentation
 3194        editor.buffer.update(cx, |buffer, cx| {
 3195            buffer.edit(
 3196                [
 3197                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3198                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3199                ],
 3200                None,
 3201                cx,
 3202            )
 3203        });
 3204
 3205        // We remove any leading spaces
 3206        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3207        editor.change_selections(None, cx, |s| {
 3208            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3209        });
 3210        editor.join_lines(&JoinLines, cx);
 3211        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3212
 3213        // We don't insert a space for a line containing only spaces
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3216
 3217        // We ignore any leading tabs
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3220
 3221        editor
 3222    });
 3223}
 3224
 3225#[gpui::test]
 3226fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3227    init_test(cx, |_| {});
 3228
 3229    cx.add_window(|cx| {
 3230        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3231        let mut editor = build_editor(buffer.clone(), cx);
 3232        let buffer = buffer.read(cx).as_singleton().unwrap();
 3233
 3234        editor.change_selections(None, cx, |s| {
 3235            s.select_ranges([
 3236                Point::new(0, 2)..Point::new(1, 1),
 3237                Point::new(1, 2)..Point::new(1, 2),
 3238                Point::new(3, 1)..Point::new(3, 2),
 3239            ])
 3240        });
 3241
 3242        editor.join_lines(&JoinLines, cx);
 3243        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3244
 3245        assert_eq!(
 3246            editor.selections.ranges::<Point>(cx),
 3247            [
 3248                Point::new(0, 7)..Point::new(0, 7),
 3249                Point::new(1, 3)..Point::new(1, 3)
 3250            ]
 3251        );
 3252        editor
 3253    });
 3254}
 3255
 3256#[gpui::test]
 3257async fn test_join_lines_with_git_diff_base(
 3258    executor: BackgroundExecutor,
 3259    cx: &mut gpui::TestAppContext,
 3260) {
 3261    init_test(cx, |_| {});
 3262
 3263    let mut cx = EditorTestContext::new(cx).await;
 3264
 3265    let diff_base = r#"
 3266        Line 0
 3267        Line 1
 3268        Line 2
 3269        Line 3
 3270        "#
 3271    .unindent();
 3272
 3273    cx.set_state(
 3274        &r#"
 3275        ˇLine 0
 3276        Line 1
 3277        Line 2
 3278        Line 3
 3279        "#
 3280        .unindent(),
 3281    );
 3282
 3283    cx.set_diff_base(Some(&diff_base));
 3284    executor.run_until_parked();
 3285
 3286    // Join lines
 3287    cx.update_editor(|editor, cx| {
 3288        editor.join_lines(&JoinLines, cx);
 3289    });
 3290    executor.run_until_parked();
 3291
 3292    cx.assert_editor_state(
 3293        &r#"
 3294        Line 0ˇ Line 1
 3295        Line 2
 3296        Line 3
 3297        "#
 3298        .unindent(),
 3299    );
 3300    // Join again
 3301    cx.update_editor(|editor, cx| {
 3302        editor.join_lines(&JoinLines, cx);
 3303    });
 3304    executor.run_until_parked();
 3305
 3306    cx.assert_editor_state(
 3307        &r#"
 3308        Line 0 Line 1ˇ Line 2
 3309        Line 3
 3310        "#
 3311        .unindent(),
 3312    );
 3313}
 3314
 3315#[gpui::test]
 3316async fn test_custom_newlines_cause_no_false_positive_diffs(
 3317    executor: BackgroundExecutor,
 3318    cx: &mut gpui::TestAppContext,
 3319) {
 3320    init_test(cx, |_| {});
 3321    let mut cx = EditorTestContext::new(cx).await;
 3322    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3323    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3324    executor.run_until_parked();
 3325
 3326    cx.update_editor(|editor, cx| {
 3327        assert_eq!(
 3328            editor
 3329                .buffer()
 3330                .read(cx)
 3331                .snapshot(cx)
 3332                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3333                .collect::<Vec<_>>(),
 3334            Vec::new(),
 3335            "Should not have any diffs for files with custom newlines"
 3336        );
 3337    });
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    // Test sort_lines_case_insensitive()
 3347    cx.set_state(indoc! {"
 3348        «z
 3349        y
 3350        x
 3351        Z
 3352        Y
 3353        Xˇ»
 3354    "});
 3355    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3356    cx.assert_editor_state(indoc! {"
 3357        «x
 3358        X
 3359        y
 3360        Y
 3361        z
 3362        Zˇ»
 3363    "});
 3364
 3365    // Test reverse_lines()
 3366    cx.set_state(indoc! {"
 3367        «5
 3368        4
 3369        3
 3370        2
 3371        1ˇ»
 3372    "});
 3373    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3374    cx.assert_editor_state(indoc! {"
 3375        «1
 3376        2
 3377        3
 3378        4
 3379        5ˇ»
 3380    "});
 3381
 3382    // Skip testing shuffle_line()
 3383
 3384    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3385    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3386
 3387    // Don't manipulate when cursor is on single line, but expand the selection
 3388    cx.set_state(indoc! {"
 3389        ddˇdd
 3390        ccc
 3391        bb
 3392        a
 3393    "});
 3394    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3395    cx.assert_editor_state(indoc! {"
 3396        «ddddˇ»
 3397        ccc
 3398        bb
 3399        a
 3400    "});
 3401
 3402    // Basic manipulate case
 3403    // Start selection moves to column 0
 3404    // End of selection shrinks to fit shorter line
 3405    cx.set_state(indoc! {"
 3406        dd«d
 3407        ccc
 3408        bb
 3409        aaaaaˇ»
 3410    "});
 3411    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3412    cx.assert_editor_state(indoc! {"
 3413        «aaaaa
 3414        bb
 3415        ccc
 3416        dddˇ»
 3417    "});
 3418
 3419    // Manipulate case with newlines
 3420    cx.set_state(indoc! {"
 3421        dd«d
 3422        ccc
 3423
 3424        bb
 3425        aaaaa
 3426
 3427        ˇ»
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «
 3432
 3433        aaaaa
 3434        bb
 3435        ccc
 3436        dddˇ»
 3437
 3438    "});
 3439
 3440    // Adding new line
 3441    cx.set_state(indoc! {"
 3442        aa«a
 3443        bbˇ»b
 3444    "});
 3445    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3446    cx.assert_editor_state(indoc! {"
 3447        «aaa
 3448        bbb
 3449        added_lineˇ»
 3450    "});
 3451
 3452    // Removing line
 3453    cx.set_state(indoc! {"
 3454        aa«a
 3455        bbbˇ»
 3456    "});
 3457    cx.update_editor(|e, cx| {
 3458        e.manipulate_lines(cx, |lines| {
 3459            lines.pop();
 3460        })
 3461    });
 3462    cx.assert_editor_state(indoc! {"
 3463        «aaaˇ»
 3464    "});
 3465
 3466    // Removing all lines
 3467    cx.set_state(indoc! {"
 3468        aa«a
 3469        bbbˇ»
 3470    "});
 3471    cx.update_editor(|e, cx| {
 3472        e.manipulate_lines(cx, |lines| {
 3473            lines.drain(..);
 3474        })
 3475    });
 3476    cx.assert_editor_state(indoc! {"
 3477        ˇ
 3478    "});
 3479}
 3480
 3481#[gpui::test]
 3482async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3483    init_test(cx, |_| {});
 3484
 3485    let mut cx = EditorTestContext::new(cx).await;
 3486
 3487    // Consider continuous selection as single selection
 3488    cx.set_state(indoc! {"
 3489        Aaa«aa
 3490        cˇ»c«c
 3491        bb
 3492        aaaˇ»aa
 3493    "});
 3494    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3495    cx.assert_editor_state(indoc! {"
 3496        «Aaaaa
 3497        ccc
 3498        bb
 3499        aaaaaˇ»
 3500    "});
 3501
 3502    cx.set_state(indoc! {"
 3503        Aaa«aa
 3504        cˇ»c«c
 3505        bb
 3506        aaaˇ»aa
 3507    "});
 3508    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3509    cx.assert_editor_state(indoc! {"
 3510        «Aaaaa
 3511        ccc
 3512        bbˇ»
 3513    "});
 3514
 3515    // Consider non continuous selection as distinct dedup operations
 3516    cx.set_state(indoc! {"
 3517        «aaaaa
 3518        bb
 3519        aaaaa
 3520        aaaaaˇ»
 3521
 3522        aaa«aaˇ»
 3523    "});
 3524    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3525    cx.assert_editor_state(indoc! {"
 3526        «aaaaa
 3527        bbˇ»
 3528
 3529        «aaaaaˇ»
 3530    "});
 3531}
 3532
 3533#[gpui::test]
 3534async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3535    init_test(cx, |_| {});
 3536
 3537    let mut cx = EditorTestContext::new(cx).await;
 3538
 3539    cx.set_state(indoc! {"
 3540        «Aaa
 3541        aAa
 3542        Aaaˇ»
 3543    "});
 3544    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3545    cx.assert_editor_state(indoc! {"
 3546        «Aaa
 3547        aAaˇ»
 3548    "});
 3549
 3550    cx.set_state(indoc! {"
 3551        «Aaa
 3552        aAa
 3553        aaAˇ»
 3554    "});
 3555    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «Aaaˇ»
 3558    "});
 3559}
 3560
 3561#[gpui::test]
 3562async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3563    init_test(cx, |_| {});
 3564
 3565    let mut cx = EditorTestContext::new(cx).await;
 3566
 3567    // Manipulate with multiple selections on a single line
 3568    cx.set_state(indoc! {"
 3569        dd«dd
 3570        cˇ»c«c
 3571        bb
 3572        aaaˇ»aa
 3573    "});
 3574    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «aaaaa
 3577        bb
 3578        ccc
 3579        ddddˇ»
 3580    "});
 3581
 3582    // Manipulate with multiple disjoin selections
 3583    cx.set_state(indoc! {"
 3584 3585        4
 3586        3
 3587        2
 3588        1ˇ»
 3589
 3590        dd«dd
 3591        ccc
 3592        bb
 3593        aaaˇ»aa
 3594    "});
 3595    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3596    cx.assert_editor_state(indoc! {"
 3597        «1
 3598        2
 3599        3
 3600        4
 3601        5ˇ»
 3602
 3603        «aaaaa
 3604        bb
 3605        ccc
 3606        ddddˇ»
 3607    "});
 3608
 3609    // Adding lines on each selection
 3610    cx.set_state(indoc! {"
 3611 3612        1ˇ»
 3613
 3614        bb«bb
 3615        aaaˇ»aa
 3616    "});
 3617    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3618    cx.assert_editor_state(indoc! {"
 3619        «2
 3620        1
 3621        added lineˇ»
 3622
 3623        «bbbb
 3624        aaaaa
 3625        added lineˇ»
 3626    "});
 3627
 3628    // Removing lines on each selection
 3629    cx.set_state(indoc! {"
 3630 3631        1ˇ»
 3632
 3633        bb«bb
 3634        aaaˇ»aa
 3635    "});
 3636    cx.update_editor(|e, cx| {
 3637        e.manipulate_lines(cx, |lines| {
 3638            lines.pop();
 3639        })
 3640    });
 3641    cx.assert_editor_state(indoc! {"
 3642        «2ˇ»
 3643
 3644        «bbbbˇ»
 3645    "});
 3646}
 3647
 3648#[gpui::test]
 3649async fn test_manipulate_text(cx: &mut TestAppContext) {
 3650    init_test(cx, |_| {});
 3651
 3652    let mut cx = EditorTestContext::new(cx).await;
 3653
 3654    // Test convert_to_upper_case()
 3655    cx.set_state(indoc! {"
 3656        «hello worldˇ»
 3657    "});
 3658    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3659    cx.assert_editor_state(indoc! {"
 3660        «HELLO WORLDˇ»
 3661    "});
 3662
 3663    // Test convert_to_lower_case()
 3664    cx.set_state(indoc! {"
 3665        «HELLO WORLDˇ»
 3666    "});
 3667    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3668    cx.assert_editor_state(indoc! {"
 3669        «hello worldˇ»
 3670    "});
 3671
 3672    // Test multiple line, single selection case
 3673    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3674    cx.set_state(indoc! {"
 3675        «The quick brown
 3676        fox jumps over
 3677        the lazy dogˇ»
 3678    "});
 3679    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3680    cx.assert_editor_state(indoc! {"
 3681        «The Quick Brown
 3682        Fox Jumps Over
 3683        The Lazy Dogˇ»
 3684    "});
 3685
 3686    // Test multiple line, single selection case
 3687    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3688    cx.set_state(indoc! {"
 3689        «The quick brown
 3690        fox jumps over
 3691        the lazy dogˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «TheQuickBrown
 3696        FoxJumpsOver
 3697        TheLazyDogˇ»
 3698    "});
 3699
 3700    // From here on out, test more complex cases of manipulate_text()
 3701
 3702    // Test no selection case - should affect words cursors are in
 3703    // Cursor at beginning, middle, and end of word
 3704    cx.set_state(indoc! {"
 3705        ˇhello big beauˇtiful worldˇ
 3706    "});
 3707    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3708    cx.assert_editor_state(indoc! {"
 3709        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3710    "});
 3711
 3712    // Test multiple selections on a single line and across multiple lines
 3713    cx.set_state(indoc! {"
 3714        «Theˇ» quick «brown
 3715        foxˇ» jumps «overˇ»
 3716        the «lazyˇ» dog
 3717    "});
 3718    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3719    cx.assert_editor_state(indoc! {"
 3720        «THEˇ» quick «BROWN
 3721        FOXˇ» jumps «OVERˇ»
 3722        the «LAZYˇ» dog
 3723    "});
 3724
 3725    // Test case where text length grows
 3726    cx.set_state(indoc! {"
 3727        «tschüߡ»
 3728    "});
 3729    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3730    cx.assert_editor_state(indoc! {"
 3731        «TSCHÜSSˇ»
 3732    "});
 3733
 3734    // Test to make sure we don't crash when text shrinks
 3735    cx.set_state(indoc! {"
 3736        aaa_bbbˇ
 3737    "});
 3738    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3739    cx.assert_editor_state(indoc! {"
 3740        «aaaBbbˇ»
 3741    "});
 3742
 3743    // Test to make sure we all aware of the fact that each word can grow and shrink
 3744    // Final selections should be aware of this fact
 3745    cx.set_state(indoc! {"
 3746        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3747    "});
 3748    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3749    cx.assert_editor_state(indoc! {"
 3750        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3751    "});
 3752
 3753    cx.set_state(indoc! {"
 3754        «hElLo, WoRld!ˇ»
 3755    "});
 3756    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3757    cx.assert_editor_state(indoc! {"
 3758        «HeLlO, wOrLD!ˇ»
 3759    "});
 3760}
 3761
 3762#[gpui::test]
 3763fn test_duplicate_line(cx: &mut TestAppContext) {
 3764    init_test(cx, |_| {});
 3765
 3766    let view = cx.add_window(|cx| {
 3767        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3768        build_editor(buffer, cx)
 3769    });
 3770    _ = view.update(cx, |view, cx| {
 3771        view.change_selections(None, cx, |s| {
 3772            s.select_display_ranges([
 3773                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3774                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3775                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3776                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3777            ])
 3778        });
 3779        view.duplicate_line_down(&DuplicateLineDown, cx);
 3780        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3781        assert_eq!(
 3782            view.selections.display_ranges(cx),
 3783            vec![
 3784                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3785                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3786                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3787                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3788            ]
 3789        );
 3790    });
 3791
 3792    let view = cx.add_window(|cx| {
 3793        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3794        build_editor(buffer, cx)
 3795    });
 3796    _ = view.update(cx, |view, cx| {
 3797        view.change_selections(None, cx, |s| {
 3798            s.select_display_ranges([
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3800                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3801            ])
 3802        });
 3803        view.duplicate_line_down(&DuplicateLineDown, cx);
 3804        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3805        assert_eq!(
 3806            view.selections.display_ranges(cx),
 3807            vec![
 3808                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3809                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3810            ]
 3811        );
 3812    });
 3813
 3814    // With `move_upwards` the selections stay in place, except for
 3815    // the lines inserted above them
 3816    let view = cx.add_window(|cx| {
 3817        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3818        build_editor(buffer, cx)
 3819    });
 3820    _ = view.update(cx, |view, cx| {
 3821        view.change_selections(None, cx, |s| {
 3822            s.select_display_ranges([
 3823                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3824                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3825                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3826                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3827            ])
 3828        });
 3829        view.duplicate_line_up(&DuplicateLineUp, cx);
 3830        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3831        assert_eq!(
 3832            view.selections.display_ranges(cx),
 3833            vec![
 3834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3835                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3836                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3837                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3838            ]
 3839        );
 3840    });
 3841
 3842    let view = cx.add_window(|cx| {
 3843        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3844        build_editor(buffer, cx)
 3845    });
 3846    _ = view.update(cx, |view, cx| {
 3847        view.change_selections(None, cx, |s| {
 3848            s.select_display_ranges([
 3849                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3850                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3851            ])
 3852        });
 3853        view.duplicate_line_up(&DuplicateLineUp, cx);
 3854        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3855        assert_eq!(
 3856            view.selections.display_ranges(cx),
 3857            vec![
 3858                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3859                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3860            ]
 3861        );
 3862    });
 3863}
 3864
 3865#[gpui::test]
 3866fn test_move_line_up_down(cx: &mut TestAppContext) {
 3867    init_test(cx, |_| {});
 3868
 3869    let view = cx.add_window(|cx| {
 3870        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3871        build_editor(buffer, cx)
 3872    });
 3873    _ = view.update(cx, |view, cx| {
 3874        view.fold_ranges(
 3875            vec![
 3876                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3877                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3878                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3879            ],
 3880            true,
 3881            cx,
 3882        );
 3883        view.change_selections(None, cx, |s| {
 3884            s.select_display_ranges([
 3885                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3886                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3887                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3888                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3889            ])
 3890        });
 3891        assert_eq!(
 3892            view.display_text(cx),
 3893            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3894        );
 3895
 3896        view.move_line_up(&MoveLineUp, cx);
 3897        assert_eq!(
 3898            view.display_text(cx),
 3899            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3900        );
 3901        assert_eq!(
 3902            view.selections.display_ranges(cx),
 3903            vec![
 3904                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3905                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3906                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3907                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3908            ]
 3909        );
 3910    });
 3911
 3912    _ = view.update(cx, |view, cx| {
 3913        view.move_line_down(&MoveLineDown, cx);
 3914        assert_eq!(
 3915            view.display_text(cx),
 3916            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3917        );
 3918        assert_eq!(
 3919            view.selections.display_ranges(cx),
 3920            vec![
 3921                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3922                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3923                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3924                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3925            ]
 3926        );
 3927    });
 3928
 3929    _ = view.update(cx, |view, cx| {
 3930        view.move_line_down(&MoveLineDown, cx);
 3931        assert_eq!(
 3932            view.display_text(cx),
 3933            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3934        );
 3935        assert_eq!(
 3936            view.selections.display_ranges(cx),
 3937            vec![
 3938                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3939                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3940                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3941                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3942            ]
 3943        );
 3944    });
 3945
 3946    _ = view.update(cx, |view, cx| {
 3947        view.move_line_up(&MoveLineUp, cx);
 3948        assert_eq!(
 3949            view.display_text(cx),
 3950            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3951        );
 3952        assert_eq!(
 3953            view.selections.display_ranges(cx),
 3954            vec![
 3955                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3956                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3957                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3958                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3959            ]
 3960        );
 3961    });
 3962}
 3963
 3964#[gpui::test]
 3965fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3966    init_test(cx, |_| {});
 3967
 3968    let editor = cx.add_window(|cx| {
 3969        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3970        build_editor(buffer, cx)
 3971    });
 3972    _ = editor.update(cx, |editor, cx| {
 3973        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3974        editor.insert_blocks(
 3975            [BlockProperties {
 3976                style: BlockStyle::Fixed,
 3977                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 3978                height: 1,
 3979                render: Box::new(|_| div().into_any()),
 3980                priority: 0,
 3981            }],
 3982            Some(Autoscroll::fit()),
 3983            cx,
 3984        );
 3985        editor.change_selections(None, cx, |s| {
 3986            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3987        });
 3988        editor.move_line_down(&MoveLineDown, cx);
 3989    });
 3990}
 3991
 3992#[gpui::test]
 3993async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 3994    init_test(cx, |_| {});
 3995
 3996    let mut cx = EditorTestContext::new(cx).await;
 3997    cx.set_state(
 3998        &"
 3999            ˇzero
 4000            one
 4001            two
 4002            three
 4003            four
 4004            five
 4005        "
 4006        .unindent(),
 4007    );
 4008
 4009    // Create a four-line block that replaces three lines of text.
 4010    cx.update_editor(|editor, cx| {
 4011        let snapshot = editor.snapshot(cx);
 4012        let snapshot = &snapshot.buffer_snapshot;
 4013        let placement = BlockPlacement::Replace(
 4014            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4015        );
 4016        editor.insert_blocks(
 4017            [BlockProperties {
 4018                placement,
 4019                height: 4,
 4020                style: BlockStyle::Sticky,
 4021                render: Box::new(|_| gpui::div().into_any_element()),
 4022                priority: 0,
 4023            }],
 4024            None,
 4025            cx,
 4026        );
 4027    });
 4028
 4029    // Move down so that the cursor touches the block.
 4030    cx.update_editor(|editor, cx| {
 4031        editor.move_down(&Default::default(), cx);
 4032    });
 4033    cx.assert_editor_state(
 4034        &"
 4035            zero
 4036            «one
 4037            two
 4038            threeˇ»
 4039            four
 4040            five
 4041        "
 4042        .unindent(),
 4043    );
 4044
 4045    // Move down past the block.
 4046    cx.update_editor(|editor, cx| {
 4047        editor.move_down(&Default::default(), cx);
 4048    });
 4049    cx.assert_editor_state(
 4050        &"
 4051            zero
 4052            one
 4053            two
 4054            three
 4055            ˇfour
 4056            five
 4057        "
 4058        .unindent(),
 4059    );
 4060}
 4061
 4062#[gpui::test]
 4063fn test_transpose(cx: &mut TestAppContext) {
 4064    init_test(cx, |_| {});
 4065
 4066    _ = cx.add_window(|cx| {
 4067        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4068        editor.set_style(EditorStyle::default(), cx);
 4069        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4070        editor.transpose(&Default::default(), cx);
 4071        assert_eq!(editor.text(cx), "bac");
 4072        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4073
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "bca");
 4076        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "bac");
 4080        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4081
 4082        editor
 4083    });
 4084
 4085    _ = cx.add_window(|cx| {
 4086        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4087        editor.set_style(EditorStyle::default(), cx);
 4088        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4089        editor.transpose(&Default::default(), cx);
 4090        assert_eq!(editor.text(cx), "acb\nde");
 4091        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4092
 4093        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4094        editor.transpose(&Default::default(), cx);
 4095        assert_eq!(editor.text(cx), "acbd\ne");
 4096        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4097
 4098        editor.transpose(&Default::default(), cx);
 4099        assert_eq!(editor.text(cx), "acbde\n");
 4100        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4101
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "acbd\ne");
 4104        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4105
 4106        editor
 4107    });
 4108
 4109    _ = cx.add_window(|cx| {
 4110        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4111        editor.set_style(EditorStyle::default(), cx);
 4112        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4113        editor.transpose(&Default::default(), cx);
 4114        assert_eq!(editor.text(cx), "bacd\ne");
 4115        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4116
 4117        editor.transpose(&Default::default(), cx);
 4118        assert_eq!(editor.text(cx), "bcade\n");
 4119        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4120
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "bcda\ne");
 4123        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4124
 4125        editor.transpose(&Default::default(), cx);
 4126        assert_eq!(editor.text(cx), "bcade\n");
 4127        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4128
 4129        editor.transpose(&Default::default(), cx);
 4130        assert_eq!(editor.text(cx), "bcaed\n");
 4131        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4132
 4133        editor
 4134    });
 4135
 4136    _ = cx.add_window(|cx| {
 4137        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4138        editor.set_style(EditorStyle::default(), cx);
 4139        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4140        editor.transpose(&Default::default(), cx);
 4141        assert_eq!(editor.text(cx), "🏀🍐✋");
 4142        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4143
 4144        editor.transpose(&Default::default(), cx);
 4145        assert_eq!(editor.text(cx), "🏀✋🍐");
 4146        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4147
 4148        editor.transpose(&Default::default(), cx);
 4149        assert_eq!(editor.text(cx), "🏀🍐✋");
 4150        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4151
 4152        editor
 4153    });
 4154}
 4155
 4156#[gpui::test]
 4157async fn test_rewrap(cx: &mut TestAppContext) {
 4158    init_test(cx, |_| {});
 4159
 4160    let mut cx = EditorTestContext::new(cx).await;
 4161
 4162    {
 4163        let language = Arc::new(Language::new(
 4164            LanguageConfig {
 4165                line_comments: vec!["// ".into()],
 4166                ..LanguageConfig::default()
 4167            },
 4168            None,
 4169        ));
 4170        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4171
 4172        let unwrapped_text = indoc! {"
 4173            // ˇ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.
 4174        "};
 4175
 4176        let wrapped_text = indoc! {"
 4177            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4178            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4179            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4180            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4181            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4182            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4183            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4184            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4185            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4186            // porttitor id. Aliquam id accumsan eros.ˇ
 4187        "};
 4188
 4189        cx.set_state(unwrapped_text);
 4190        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4191        cx.assert_editor_state(wrapped_text);
 4192    }
 4193
 4194    // Test that rewrapping works inside of a selection
 4195    {
 4196        let language = Arc::new(Language::new(
 4197            LanguageConfig {
 4198                line_comments: vec!["// ".into()],
 4199                ..LanguageConfig::default()
 4200            },
 4201            None,
 4202        ));
 4203        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4204
 4205        let unwrapped_text = indoc! {"
 4206            «// 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.ˇ»
 4207        "};
 4208
 4209        let wrapped_text = indoc! {"
 4210            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4211            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4212            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4213            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4214            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4215            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4216            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4217            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4218            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4219            // porttitor id. Aliquam id accumsan eros.ˇ
 4220        "};
 4221
 4222        cx.set_state(unwrapped_text);
 4223        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4224        cx.assert_editor_state(wrapped_text);
 4225    }
 4226
 4227    // Test that cursors that expand to the same region are collapsed.
 4228    {
 4229        let language = Arc::new(Language::new(
 4230            LanguageConfig {
 4231                line_comments: vec!["// ".into()],
 4232                ..LanguageConfig::default()
 4233            },
 4234            None,
 4235        ));
 4236        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4237
 4238        let unwrapped_text = indoc! {"
 4239            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4240            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4241            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4242            // ˇ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.
 4243        "};
 4244
 4245        let wrapped_text = indoc! {"
 4246            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4247            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4248            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4249            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4250            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4251            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4252            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4253            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4254            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4255            // porttitor id. Aliquam id accumsan eros.ˇ
 4256        "};
 4257
 4258        cx.set_state(unwrapped_text);
 4259        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4260        cx.assert_editor_state(wrapped_text);
 4261    }
 4262
 4263    // Test that non-contiguous selections are treated separately.
 4264    {
 4265        let language = Arc::new(Language::new(
 4266            LanguageConfig {
 4267                line_comments: vec!["// ".into()],
 4268                ..LanguageConfig::default()
 4269            },
 4270            None,
 4271        ));
 4272        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4273
 4274        let unwrapped_text = indoc! {"
 4275            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4276            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4277            //
 4278            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4279            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4280        "};
 4281
 4282        let wrapped_text = indoc! {"
 4283            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4284            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4285            // auctor, eu lacinia sapien scelerisque.ˇ
 4286            //
 4287            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4288            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4289            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4290            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4291            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4292            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4293            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ
 4294        "};
 4295
 4296        cx.set_state(unwrapped_text);
 4297        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4298        cx.assert_editor_state(wrapped_text);
 4299    }
 4300
 4301    // Test that different comment prefixes are supported.
 4302    {
 4303        let language = Arc::new(Language::new(
 4304            LanguageConfig {
 4305                line_comments: vec!["# ".into()],
 4306                ..LanguageConfig::default()
 4307            },
 4308            None,
 4309        ));
 4310        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4311
 4312        let unwrapped_text = indoc! {"
 4313            # ˇ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.
 4314        "};
 4315
 4316        let wrapped_text = indoc! {"
 4317            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4318            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4319            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4320            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4321            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4322            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4323            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4324            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4325            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4326            # accumsan eros.ˇ
 4327        "};
 4328
 4329        cx.set_state(unwrapped_text);
 4330        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4331        cx.assert_editor_state(wrapped_text);
 4332    }
 4333
 4334    // Test that rewrapping is ignored outside of comments in most languages.
 4335    {
 4336        let language = Arc::new(Language::new(
 4337            LanguageConfig {
 4338                line_comments: vec!["// ".into(), "/// ".into()],
 4339                ..LanguageConfig::default()
 4340            },
 4341            Some(tree_sitter_rust::LANGUAGE.into()),
 4342        ));
 4343        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4344
 4345        let unwrapped_text = indoc! {"
 4346            /// Adds two numbers.
 4347            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4348            fn add(a: u32, b: u32) -> u32 {
 4349                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ˇ
 4350            }
 4351        "};
 4352
 4353        let wrapped_text = indoc! {"
 4354            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4355            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4356            fn add(a: u32, b: u32) -> u32 {
 4357                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ˇ
 4358            }
 4359        "};
 4360
 4361        cx.set_state(unwrapped_text);
 4362        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4363        cx.assert_editor_state(wrapped_text);
 4364    }
 4365
 4366    // Test that rewrapping works in Markdown and Plain Text languages.
 4367    {
 4368        let markdown_language = Arc::new(Language::new(
 4369            LanguageConfig {
 4370                name: "Markdown".into(),
 4371                ..LanguageConfig::default()
 4372            },
 4373            None,
 4374        ));
 4375        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4376
 4377        let unwrapped_text = indoc! {"
 4378            # Hello
 4379
 4380            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.
 4381        "};
 4382
 4383        let wrapped_text = indoc! {"
 4384            # Hello
 4385
 4386            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4387            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4388            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4389            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4390            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4391            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4392            Integer sit amet scelerisque nisi.ˇ
 4393        "};
 4394
 4395        cx.set_state(unwrapped_text);
 4396        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4397        cx.assert_editor_state(wrapped_text);
 4398
 4399        let plaintext_language = Arc::new(Language::new(
 4400            LanguageConfig {
 4401                name: "Plain Text".into(),
 4402                ..LanguageConfig::default()
 4403            },
 4404            None,
 4405        ));
 4406        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4407
 4408        let unwrapped_text = indoc! {"
 4409            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.
 4410        "};
 4411
 4412        let wrapped_text = indoc! {"
 4413            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4414            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4415            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4416            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4417            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4418            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4419            Integer sit amet scelerisque nisi.ˇ
 4420        "};
 4421
 4422        cx.set_state(unwrapped_text);
 4423        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4424        cx.assert_editor_state(wrapped_text);
 4425    }
 4426
 4427    // Test rewrapping unaligned comments in a selection.
 4428    {
 4429        let language = Arc::new(Language::new(
 4430            LanguageConfig {
 4431                line_comments: vec!["// ".into(), "/// ".into()],
 4432                ..LanguageConfig::default()
 4433            },
 4434            Some(tree_sitter_rust::LANGUAGE.into()),
 4435        ));
 4436        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4437
 4438        let unwrapped_text = indoc! {"
 4439            fn foo() {
 4440                if true {
 4441            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4442            // Praesent semper egestas tellus id dignissim.ˇ»
 4443                    do_something();
 4444                } else {
 4445                    //
 4446                }
 4447            }
 4448        "};
 4449
 4450        let wrapped_text = indoc! {"
 4451            fn foo() {
 4452                if true {
 4453                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4454                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4455                    // egestas tellus id dignissim.ˇ
 4456                    do_something();
 4457                } else {
 4458                    //
 4459                }
 4460            }
 4461        "};
 4462
 4463        cx.set_state(unwrapped_text);
 4464        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4465        cx.assert_editor_state(wrapped_text);
 4466
 4467        let unwrapped_text = indoc! {"
 4468            fn foo() {
 4469                if true {
 4470            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4471            // Praesent semper egestas tellus id dignissim.»
 4472                    do_something();
 4473                } else {
 4474                    //
 4475                }
 4476
 4477            }
 4478        "};
 4479
 4480        let wrapped_text = indoc! {"
 4481            fn foo() {
 4482                if true {
 4483                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4484                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4485                    // egestas tellus id dignissim.ˇ
 4486                    do_something();
 4487                } else {
 4488                    //
 4489                }
 4490
 4491            }
 4492        "};
 4493
 4494        cx.set_state(unwrapped_text);
 4495        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4496        cx.assert_editor_state(wrapped_text);
 4497    }
 4498}
 4499
 4500#[gpui::test]
 4501async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4502    init_test(cx, |_| {});
 4503
 4504    let mut cx = EditorTestContext::new(cx).await;
 4505
 4506    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4507    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4508    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4509
 4510    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4511    cx.set_state("two ˇfour ˇsix ˇ");
 4512    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4513    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4514
 4515    // Paste again but with only two cursors. Since the number of cursors doesn't
 4516    // match the number of slices in the clipboard, the entire clipboard text
 4517    // is pasted at each cursor.
 4518    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4519    cx.update_editor(|e, cx| {
 4520        e.handle_input("( ", cx);
 4521        e.paste(&Paste, cx);
 4522        e.handle_input(") ", cx);
 4523    });
 4524    cx.assert_editor_state(
 4525        &([
 4526            "( one✅ ",
 4527            "three ",
 4528            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4529            "three ",
 4530            "five ) ˇ",
 4531        ]
 4532        .join("\n")),
 4533    );
 4534
 4535    // Cut with three selections, one of which is full-line.
 4536    cx.set_state(indoc! {"
 4537        1«2ˇ»3
 4538        4ˇ567
 4539        «8ˇ»9"});
 4540    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4541    cx.assert_editor_state(indoc! {"
 4542        1ˇ3
 4543        ˇ9"});
 4544
 4545    // Paste with three selections, noticing how the copied selection that was full-line
 4546    // gets inserted before the second cursor.
 4547    cx.set_state(indoc! {"
 4548        1ˇ3
 4549 4550        «oˇ»ne"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        12ˇ3
 4554        4567
 4555 4556        8ˇne"});
 4557
 4558    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4559    cx.set_state(indoc! {"
 4560        The quick brown
 4561        fox juˇmps over
 4562        the lazy dog"});
 4563    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4564    assert_eq!(
 4565        cx.read_from_clipboard()
 4566            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4567        Some("fox jumps over\n".to_string())
 4568    );
 4569
 4570    // Paste with three selections, noticing how the copied full-line selection is inserted
 4571    // before the empty selections but replaces the selection that is non-empty.
 4572    cx.set_state(indoc! {"
 4573        Tˇhe quick brown
 4574        «foˇ»x jumps over
 4575        tˇhe lazy dog"});
 4576    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4577    cx.assert_editor_state(indoc! {"
 4578        fox jumps over
 4579        Tˇhe quick brown
 4580        fox jumps over
 4581        ˇx jumps over
 4582        fox jumps over
 4583        tˇhe lazy dog"});
 4584}
 4585
 4586#[gpui::test]
 4587async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4588    init_test(cx, |_| {});
 4589
 4590    let mut cx = EditorTestContext::new(cx).await;
 4591    let language = Arc::new(Language::new(
 4592        LanguageConfig::default(),
 4593        Some(tree_sitter_rust::LANGUAGE.into()),
 4594    ));
 4595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4596
 4597    // Cut an indented block, without the leading whitespace.
 4598    cx.set_state(indoc! {"
 4599        const a: B = (
 4600            c(),
 4601            «d(
 4602                e,
 4603                f
 4604            )ˇ»
 4605        );
 4606    "});
 4607    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4608    cx.assert_editor_state(indoc! {"
 4609        const a: B = (
 4610            c(),
 4611            ˇ
 4612        );
 4613    "});
 4614
 4615    // Paste it at the same position.
 4616    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4617    cx.assert_editor_state(indoc! {"
 4618        const a: B = (
 4619            c(),
 4620            d(
 4621                e,
 4622                f
 4623 4624        );
 4625    "});
 4626
 4627    // Paste it at a line with a lower indent level.
 4628    cx.set_state(indoc! {"
 4629        ˇ
 4630        const a: B = (
 4631            c(),
 4632        );
 4633    "});
 4634    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4635    cx.assert_editor_state(indoc! {"
 4636        d(
 4637            e,
 4638            f
 4639 4640        const a: B = (
 4641            c(),
 4642        );
 4643    "});
 4644
 4645    // Cut an indented block, with the leading whitespace.
 4646    cx.set_state(indoc! {"
 4647        const a: B = (
 4648            c(),
 4649        «    d(
 4650                e,
 4651                f
 4652            )
 4653        ˇ»);
 4654    "});
 4655    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4656    cx.assert_editor_state(indoc! {"
 4657        const a: B = (
 4658            c(),
 4659        ˇ);
 4660    "});
 4661
 4662    // Paste it at the same position.
 4663    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        const a: B = (
 4666            c(),
 4667            d(
 4668                e,
 4669                f
 4670            )
 4671        ˇ);
 4672    "});
 4673
 4674    // Paste it at a line with a higher indent level.
 4675    cx.set_state(indoc! {"
 4676        const a: B = (
 4677            c(),
 4678            d(
 4679                e,
 4680 4681            )
 4682        );
 4683    "});
 4684    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4685    cx.assert_editor_state(indoc! {"
 4686        const a: B = (
 4687            c(),
 4688            d(
 4689                e,
 4690                f    d(
 4691                    e,
 4692                    f
 4693                )
 4694        ˇ
 4695            )
 4696        );
 4697    "});
 4698}
 4699
 4700#[gpui::test]
 4701fn test_select_all(cx: &mut TestAppContext) {
 4702    init_test(cx, |_| {});
 4703
 4704    let view = cx.add_window(|cx| {
 4705        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4706        build_editor(buffer, cx)
 4707    });
 4708    _ = view.update(cx, |view, cx| {
 4709        view.select_all(&SelectAll, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4713        );
 4714    });
 4715}
 4716
 4717#[gpui::test]
 4718fn test_select_line(cx: &mut TestAppContext) {
 4719    init_test(cx, |_| {});
 4720
 4721    let view = cx.add_window(|cx| {
 4722        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4723        build_editor(buffer, cx)
 4724    });
 4725    _ = view.update(cx, |view, cx| {
 4726        view.change_selections(None, cx, |s| {
 4727            s.select_display_ranges([
 4728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4730                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4731                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4732            ])
 4733        });
 4734        view.select_line(&SelectLine, cx);
 4735        assert_eq!(
 4736            view.selections.display_ranges(cx),
 4737            vec![
 4738                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4739                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4740            ]
 4741        );
 4742    });
 4743
 4744    _ = view.update(cx, |view, cx| {
 4745        view.select_line(&SelectLine, cx);
 4746        assert_eq!(
 4747            view.selections.display_ranges(cx),
 4748            vec![
 4749                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4750                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4751            ]
 4752        );
 4753    });
 4754
 4755    _ = view.update(cx, |view, cx| {
 4756        view.select_line(&SelectLine, cx);
 4757        assert_eq!(
 4758            view.selections.display_ranges(cx),
 4759            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4760        );
 4761    });
 4762}
 4763
 4764#[gpui::test]
 4765fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4766    init_test(cx, |_| {});
 4767
 4768    let view = cx.add_window(|cx| {
 4769        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4770        build_editor(buffer, cx)
 4771    });
 4772    _ = view.update(cx, |view, cx| {
 4773        view.fold_ranges(
 4774            vec![
 4775                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4776                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4777                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4778            ],
 4779            true,
 4780            cx,
 4781        );
 4782        view.change_selections(None, cx, |s| {
 4783            s.select_display_ranges([
 4784                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4785                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4786                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4787                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4788            ])
 4789        });
 4790        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4791    });
 4792
 4793    _ = view.update(cx, |view, cx| {
 4794        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4795        assert_eq!(
 4796            view.display_text(cx),
 4797            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4798        );
 4799        assert_eq!(
 4800            view.selections.display_ranges(cx),
 4801            [
 4802                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4803                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4804                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4805                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4806            ]
 4807        );
 4808    });
 4809
 4810    _ = view.update(cx, |view, cx| {
 4811        view.change_selections(None, cx, |s| {
 4812            s.select_display_ranges([
 4813                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4814            ])
 4815        });
 4816        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4817        assert_eq!(
 4818            view.display_text(cx),
 4819            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4820        );
 4821        assert_eq!(
 4822            view.selections.display_ranges(cx),
 4823            [
 4824                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4825                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4826                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4827                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4828                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4829                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4830                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4831                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4832            ]
 4833        );
 4834    });
 4835}
 4836
 4837#[gpui::test]
 4838async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4839    init_test(cx, |_| {});
 4840
 4841    let mut cx = EditorTestContext::new(cx).await;
 4842
 4843    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4844    cx.set_state(indoc!(
 4845        r#"abc
 4846           defˇghi
 4847
 4848           jk
 4849           nlmo
 4850           "#
 4851    ));
 4852
 4853    cx.update_editor(|editor, cx| {
 4854        editor.add_selection_above(&Default::default(), cx);
 4855    });
 4856
 4857    cx.assert_editor_state(indoc!(
 4858        r#"abcˇ
 4859           defˇghi
 4860
 4861           jk
 4862           nlmo
 4863           "#
 4864    ));
 4865
 4866    cx.update_editor(|editor, cx| {
 4867        editor.add_selection_above(&Default::default(), cx);
 4868    });
 4869
 4870    cx.assert_editor_state(indoc!(
 4871        r#"abcˇ
 4872            defˇghi
 4873
 4874            jk
 4875            nlmo
 4876            "#
 4877    ));
 4878
 4879    cx.update_editor(|view, cx| {
 4880        view.add_selection_below(&Default::default(), cx);
 4881    });
 4882
 4883    cx.assert_editor_state(indoc!(
 4884        r#"abc
 4885           defˇghi
 4886
 4887           jk
 4888           nlmo
 4889           "#
 4890    ));
 4891
 4892    cx.update_editor(|view, cx| {
 4893        view.undo_selection(&Default::default(), cx);
 4894    });
 4895
 4896    cx.assert_editor_state(indoc!(
 4897        r#"abcˇ
 4898           defˇghi
 4899
 4900           jk
 4901           nlmo
 4902           "#
 4903    ));
 4904
 4905    cx.update_editor(|view, cx| {
 4906        view.redo_selection(&Default::default(), cx);
 4907    });
 4908
 4909    cx.assert_editor_state(indoc!(
 4910        r#"abc
 4911           defˇghi
 4912
 4913           jk
 4914           nlmo
 4915           "#
 4916    ));
 4917
 4918    cx.update_editor(|view, cx| {
 4919        view.add_selection_below(&Default::default(), cx);
 4920    });
 4921
 4922    cx.assert_editor_state(indoc!(
 4923        r#"abc
 4924           defˇghi
 4925
 4926           jk
 4927           nlmˇo
 4928           "#
 4929    ));
 4930
 4931    cx.update_editor(|view, cx| {
 4932        view.add_selection_below(&Default::default(), cx);
 4933    });
 4934
 4935    cx.assert_editor_state(indoc!(
 4936        r#"abc
 4937           defˇghi
 4938
 4939           jk
 4940           nlmˇo
 4941           "#
 4942    ));
 4943
 4944    // change selections
 4945    cx.set_state(indoc!(
 4946        r#"abc
 4947           def«ˇg»hi
 4948
 4949           jk
 4950           nlmo
 4951           "#
 4952    ));
 4953
 4954    cx.update_editor(|view, cx| {
 4955        view.add_selection_below(&Default::default(), cx);
 4956    });
 4957
 4958    cx.assert_editor_state(indoc!(
 4959        r#"abc
 4960           def«ˇg»hi
 4961
 4962           jk
 4963           nlm«ˇo»
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|view, cx| {
 4968        view.add_selection_below(&Default::default(), cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abc
 4973           def«ˇg»hi
 4974
 4975           jk
 4976           nlm«ˇo»
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|view, cx| {
 4981        view.add_selection_above(&Default::default(), cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abc
 4986           def«ˇg»hi
 4987
 4988           jk
 4989           nlmo
 4990           "#
 4991    ));
 4992
 4993    cx.update_editor(|view, cx| {
 4994        view.add_selection_above(&Default::default(), cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"abc
 4999           def«ˇg»hi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    // Change selections again
 5007    cx.set_state(indoc!(
 5008        r#"a«bc
 5009           defgˇ»hi
 5010
 5011           jk
 5012           nlmo
 5013           "#
 5014    ));
 5015
 5016    cx.update_editor(|view, cx| {
 5017        view.add_selection_below(&Default::default(), cx);
 5018    });
 5019
 5020    cx.assert_editor_state(indoc!(
 5021        r#"a«bcˇ»
 5022           d«efgˇ»hi
 5023
 5024           j«kˇ»
 5025           nlmo
 5026           "#
 5027    ));
 5028
 5029    cx.update_editor(|view, cx| {
 5030        view.add_selection_below(&Default::default(), cx);
 5031    });
 5032    cx.assert_editor_state(indoc!(
 5033        r#"a«bcˇ»
 5034           d«efgˇ»hi
 5035
 5036           j«kˇ»
 5037           n«lmoˇ»
 5038           "#
 5039    ));
 5040    cx.update_editor(|view, cx| {
 5041        view.add_selection_above(&Default::default(), cx);
 5042    });
 5043
 5044    cx.assert_editor_state(indoc!(
 5045        r#"a«bcˇ»
 5046           d«efgˇ»hi
 5047
 5048           j«kˇ»
 5049           nlmo
 5050           "#
 5051    ));
 5052
 5053    // Change selections again
 5054    cx.set_state(indoc!(
 5055        r#"abc
 5056           d«ˇefghi
 5057
 5058           jk
 5059           nlm»o
 5060           "#
 5061    ));
 5062
 5063    cx.update_editor(|view, cx| {
 5064        view.add_selection_above(&Default::default(), cx);
 5065    });
 5066
 5067    cx.assert_editor_state(indoc!(
 5068        r#"a«ˇbc»
 5069           d«ˇef»ghi
 5070
 5071           j«ˇk»
 5072           n«ˇlm»o
 5073           "#
 5074    ));
 5075
 5076    cx.update_editor(|view, cx| {
 5077        view.add_selection_below(&Default::default(), cx);
 5078    });
 5079
 5080    cx.assert_editor_state(indoc!(
 5081        r#"abc
 5082           d«ˇef»ghi
 5083
 5084           j«ˇk»
 5085           n«ˇlm»o
 5086           "#
 5087    ));
 5088}
 5089
 5090#[gpui::test]
 5091async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5092    init_test(cx, |_| {});
 5093
 5094    let mut cx = EditorTestContext::new(cx).await;
 5095    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5096
 5097    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5098        .unwrap();
 5099    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5104
 5105    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5106    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5107
 5108    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5109    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5110
 5111    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5112        .unwrap();
 5113    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5114
 5115    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5118}
 5119
 5120#[gpui::test]
 5121async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5122    init_test(cx, |_| {});
 5123
 5124    let mut cx = EditorTestContext::new(cx).await;
 5125    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5126
 5127    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5128        .unwrap();
 5129    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5130}
 5131
 5132#[gpui::test]
 5133async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5134    init_test(cx, |_| {});
 5135
 5136    let mut cx = EditorTestContext::new(cx).await;
 5137    cx.set_state(
 5138        r#"let foo = 2;
 5139lˇet foo = 2;
 5140let fooˇ = 2;
 5141let foo = 2;
 5142let foo = ˇ2;"#,
 5143    );
 5144
 5145    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5146        .unwrap();
 5147    cx.assert_editor_state(
 5148        r#"let foo = 2;
 5149«letˇ» foo = 2;
 5150let «fooˇ» = 2;
 5151let foo = 2;
 5152let foo = «2ˇ»;"#,
 5153    );
 5154
 5155    // noop for multiple selections with different contents
 5156    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5157        .unwrap();
 5158    cx.assert_editor_state(
 5159        r#"let foo = 2;
 5160«letˇ» foo = 2;
 5161let «fooˇ» = 2;
 5162let foo = 2;
 5163let foo = «2ˇ»;"#,
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let mut cx =
 5172        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5173
 5174    cx.assert_editor_state(indoc! {"
 5175        ˇbbb
 5176        ccc
 5177
 5178        bbb
 5179        ccc
 5180        "});
 5181    cx.dispatch_action(SelectPrevious::default());
 5182    cx.assert_editor_state(indoc! {"
 5183                «bbbˇ»
 5184                ccc
 5185
 5186                bbb
 5187                ccc
 5188                "});
 5189    cx.dispatch_action(SelectPrevious::default());
 5190    cx.assert_editor_state(indoc! {"
 5191                «bbbˇ»
 5192                ccc
 5193
 5194                «bbbˇ»
 5195                ccc
 5196                "});
 5197}
 5198
 5199#[gpui::test]
 5200async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5201    init_test(cx, |_| {});
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5205
 5206    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5207        .unwrap();
 5208    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5209
 5210    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5211        .unwrap();
 5212    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5213
 5214    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5215    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5216
 5217    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5218    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5219
 5220    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5221        .unwrap();
 5222    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5223
 5224    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5225        .unwrap();
 5226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5227
 5228    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5229        .unwrap();
 5230    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5231}
 5232
 5233#[gpui::test]
 5234async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5235    init_test(cx, |_| {});
 5236
 5237    let mut cx = EditorTestContext::new(cx).await;
 5238    cx.set_state(
 5239        r#"let foo = 2;
 5240lˇet foo = 2;
 5241let fooˇ = 2;
 5242let foo = 2;
 5243let foo = ˇ2;"#,
 5244    );
 5245
 5246    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5247        .unwrap();
 5248    cx.assert_editor_state(
 5249        r#"let foo = 2;
 5250«letˇ» foo = 2;
 5251let «fooˇ» = 2;
 5252let foo = 2;
 5253let foo = «2ˇ»;"#,
 5254    );
 5255
 5256    // noop for multiple selections with different contents
 5257    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5258        .unwrap();
 5259    cx.assert_editor_state(
 5260        r#"let foo = 2;
 5261«letˇ» foo = 2;
 5262let «fooˇ» = 2;
 5263let foo = 2;
 5264let foo = «2ˇ»;"#,
 5265    );
 5266}
 5267
 5268#[gpui::test]
 5269async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5270    init_test(cx, |_| {});
 5271
 5272    let mut cx = EditorTestContext::new(cx).await;
 5273    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5274
 5275    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5276        .unwrap();
 5277    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5278
 5279    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5280        .unwrap();
 5281    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5282
 5283    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5284    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5285
 5286    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5287    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5288
 5289    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5290        .unwrap();
 5291    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5292
 5293    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5294        .unwrap();
 5295    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5296}
 5297
 5298#[gpui::test]
 5299async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5300    init_test(cx, |_| {});
 5301
 5302    let language = Arc::new(Language::new(
 5303        LanguageConfig::default(),
 5304        Some(tree_sitter_rust::LANGUAGE.into()),
 5305    ));
 5306
 5307    let text = r#"
 5308        use mod1::mod2::{mod3, mod4};
 5309
 5310        fn fn_1(param1: bool, param2: &str) {
 5311            let var1 = "text";
 5312        }
 5313    "#
 5314    .unindent();
 5315
 5316    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5317    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5318    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5319
 5320    editor
 5321        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5322        .await;
 5323
 5324    editor.update(cx, |view, cx| {
 5325        view.change_selections(None, cx, |s| {
 5326            s.select_display_ranges([
 5327                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5328                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5329                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5330            ]);
 5331        });
 5332        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5333    });
 5334    editor.update(cx, |editor, cx| {
 5335        assert_text_with_selections(
 5336            editor,
 5337            indoc! {r#"
 5338                use mod1::mod2::{mod3, «mod4ˇ»};
 5339
 5340                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5341                    let var1 = "«textˇ»";
 5342                }
 5343            "#},
 5344            cx,
 5345        );
 5346    });
 5347
 5348    editor.update(cx, |view, cx| {
 5349        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5350    });
 5351    editor.update(cx, |editor, cx| {
 5352        assert_text_with_selections(
 5353            editor,
 5354            indoc! {r#"
 5355                use mod1::mod2::«{mod3, mod4}ˇ»;
 5356
 5357                «ˇfn fn_1(param1: bool, param2: &str) {
 5358                    let var1 = "text";
 5359 5360            "#},
 5361            cx,
 5362        );
 5363    });
 5364
 5365    editor.update(cx, |view, cx| {
 5366        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5367    });
 5368    assert_eq!(
 5369        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5370        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5371    );
 5372
 5373    // Trying to expand the selected syntax node one more time has no effect.
 5374    editor.update(cx, |view, cx| {
 5375        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5376    });
 5377    assert_eq!(
 5378        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5379        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5380    );
 5381
 5382    editor.update(cx, |view, cx| {
 5383        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5384    });
 5385    editor.update(cx, |editor, cx| {
 5386        assert_text_with_selections(
 5387            editor,
 5388            indoc! {r#"
 5389                use mod1::mod2::«{mod3, mod4}ˇ»;
 5390
 5391                «ˇfn fn_1(param1: bool, param2: &str) {
 5392                    let var1 = "text";
 5393 5394            "#},
 5395            cx,
 5396        );
 5397    });
 5398
 5399    editor.update(cx, |view, cx| {
 5400        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5401    });
 5402    editor.update(cx, |editor, cx| {
 5403        assert_text_with_selections(
 5404            editor,
 5405            indoc! {r#"
 5406                use mod1::mod2::{mod3, «mod4ˇ»};
 5407
 5408                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5409                    let var1 = "«textˇ»";
 5410                }
 5411            "#},
 5412            cx,
 5413        );
 5414    });
 5415
 5416    editor.update(cx, |view, cx| {
 5417        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5418    });
 5419    editor.update(cx, |editor, cx| {
 5420        assert_text_with_selections(
 5421            editor,
 5422            indoc! {r#"
 5423                use mod1::mod2::{mod3, mo«ˇ»d4};
 5424
 5425                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5426                    let var1 = "te«ˇ»xt";
 5427                }
 5428            "#},
 5429            cx,
 5430        );
 5431    });
 5432
 5433    // Trying to shrink the selected syntax node one more time has no effect.
 5434    editor.update(cx, |view, cx| {
 5435        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5436    });
 5437    editor.update(cx, |editor, cx| {
 5438        assert_text_with_selections(
 5439            editor,
 5440            indoc! {r#"
 5441                use mod1::mod2::{mod3, mo«ˇ»d4};
 5442
 5443                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5444                    let var1 = "te«ˇ»xt";
 5445                }
 5446            "#},
 5447            cx,
 5448        );
 5449    });
 5450
 5451    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5452    // a fold.
 5453    editor.update(cx, |view, cx| {
 5454        view.fold_ranges(
 5455            vec![
 5456                (
 5457                    Point::new(0, 21)..Point::new(0, 24),
 5458                    FoldPlaceholder::test(),
 5459                ),
 5460                (
 5461                    Point::new(3, 20)..Point::new(3, 22),
 5462                    FoldPlaceholder::test(),
 5463                ),
 5464            ],
 5465            true,
 5466            cx,
 5467        );
 5468        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5469    });
 5470    editor.update(cx, |editor, cx| {
 5471        assert_text_with_selections(
 5472            editor,
 5473            indoc! {r#"
 5474                use mod1::mod2::«{mod3, mod4}ˇ»;
 5475
 5476                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5477                    «let var1 = "text";ˇ»
 5478                }
 5479            "#},
 5480            cx,
 5481        );
 5482    });
 5483}
 5484
 5485#[gpui::test]
 5486async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5487    init_test(cx, |_| {});
 5488
 5489    let language = Arc::new(
 5490        Language::new(
 5491            LanguageConfig {
 5492                brackets: BracketPairConfig {
 5493                    pairs: vec![
 5494                        BracketPair {
 5495                            start: "{".to_string(),
 5496                            end: "}".to_string(),
 5497                            close: false,
 5498                            surround: false,
 5499                            newline: true,
 5500                        },
 5501                        BracketPair {
 5502                            start: "(".to_string(),
 5503                            end: ")".to_string(),
 5504                            close: false,
 5505                            surround: false,
 5506                            newline: true,
 5507                        },
 5508                    ],
 5509                    ..Default::default()
 5510                },
 5511                ..Default::default()
 5512            },
 5513            Some(tree_sitter_rust::LANGUAGE.into()),
 5514        )
 5515        .with_indents_query(
 5516            r#"
 5517                (_ "(" ")" @end) @indent
 5518                (_ "{" "}" @end) @indent
 5519            "#,
 5520        )
 5521        .unwrap(),
 5522    );
 5523
 5524    let text = "fn a() {}";
 5525
 5526    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5527    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5528    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5529    editor
 5530        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5531        .await;
 5532
 5533    editor.update(cx, |editor, cx| {
 5534        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5535        editor.newline(&Newline, cx);
 5536        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5537        assert_eq!(
 5538            editor.selections.ranges(cx),
 5539            &[
 5540                Point::new(1, 4)..Point::new(1, 4),
 5541                Point::new(3, 4)..Point::new(3, 4),
 5542                Point::new(5, 0)..Point::new(5, 0)
 5543            ]
 5544        );
 5545    });
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    let language = Arc::new(Language::new(
 5555        LanguageConfig {
 5556            brackets: BracketPairConfig {
 5557                pairs: vec![
 5558                    BracketPair {
 5559                        start: "{".to_string(),
 5560                        end: "}".to_string(),
 5561                        close: true,
 5562                        surround: true,
 5563                        newline: true,
 5564                    },
 5565                    BracketPair {
 5566                        start: "(".to_string(),
 5567                        end: ")".to_string(),
 5568                        close: true,
 5569                        surround: true,
 5570                        newline: true,
 5571                    },
 5572                    BracketPair {
 5573                        start: "/*".to_string(),
 5574                        end: " */".to_string(),
 5575                        close: true,
 5576                        surround: true,
 5577                        newline: true,
 5578                    },
 5579                    BracketPair {
 5580                        start: "[".to_string(),
 5581                        end: "]".to_string(),
 5582                        close: false,
 5583                        surround: false,
 5584                        newline: true,
 5585                    },
 5586                    BracketPair {
 5587                        start: "\"".to_string(),
 5588                        end: "\"".to_string(),
 5589                        close: true,
 5590                        surround: true,
 5591                        newline: false,
 5592                    },
 5593                    BracketPair {
 5594                        start: "<".to_string(),
 5595                        end: ">".to_string(),
 5596                        close: false,
 5597                        surround: true,
 5598                        newline: true,
 5599                    },
 5600                ],
 5601                ..Default::default()
 5602            },
 5603            autoclose_before: "})]".to_string(),
 5604            ..Default::default()
 5605        },
 5606        Some(tree_sitter_rust::LANGUAGE.into()),
 5607    ));
 5608
 5609    cx.language_registry().add(language.clone());
 5610    cx.update_buffer(|buffer, cx| {
 5611        buffer.set_language(Some(language), cx);
 5612    });
 5613
 5614    cx.set_state(
 5615        &r#"
 5616            🏀ˇ
 5617            εˇ
 5618            ❤️ˇ
 5619        "#
 5620        .unindent(),
 5621    );
 5622
 5623    // autoclose multiple nested brackets at multiple cursors
 5624    cx.update_editor(|view, cx| {
 5625        view.handle_input("{", cx);
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628    });
 5629    cx.assert_editor_state(
 5630        &"
 5631            🏀{{{ˇ}}}
 5632            ε{{{ˇ}}}
 5633            ❤️{{{ˇ}}}
 5634        "
 5635        .unindent(),
 5636    );
 5637
 5638    // insert a different closing bracket
 5639    cx.update_editor(|view, cx| {
 5640        view.handle_input(")", cx);
 5641    });
 5642    cx.assert_editor_state(
 5643        &"
 5644            🏀{{{)ˇ}}}
 5645            ε{{{)ˇ}}}
 5646            ❤️{{{)ˇ}}}
 5647        "
 5648        .unindent(),
 5649    );
 5650
 5651    // skip over the auto-closed brackets when typing a closing bracket
 5652    cx.update_editor(|view, cx| {
 5653        view.move_right(&MoveRight, cx);
 5654        view.handle_input("}", cx);
 5655        view.handle_input("}", cx);
 5656        view.handle_input("}", cx);
 5657    });
 5658    cx.assert_editor_state(
 5659        &"
 5660            🏀{{{)}}}}ˇ
 5661            ε{{{)}}}}ˇ
 5662            ❤️{{{)}}}}ˇ
 5663        "
 5664        .unindent(),
 5665    );
 5666
 5667    // autoclose multi-character pairs
 5668    cx.set_state(
 5669        &"
 5670            ˇ
 5671            ˇ
 5672        "
 5673        .unindent(),
 5674    );
 5675    cx.update_editor(|view, cx| {
 5676        view.handle_input("/", cx);
 5677        view.handle_input("*", cx);
 5678    });
 5679    cx.assert_editor_state(
 5680        &"
 5681            /*ˇ */
 5682            /*ˇ */
 5683        "
 5684        .unindent(),
 5685    );
 5686
 5687    // one cursor autocloses a multi-character pair, one cursor
 5688    // does not autoclose.
 5689    cx.set_state(
 5690        &"
 5691 5692            ˇ
 5693        "
 5694        .unindent(),
 5695    );
 5696    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5697    cx.assert_editor_state(
 5698        &"
 5699            /*ˇ */
 5700 5701        "
 5702        .unindent(),
 5703    );
 5704
 5705    // Don't autoclose if the next character isn't whitespace and isn't
 5706    // listed in the language's "autoclose_before" section.
 5707    cx.set_state("ˇa b");
 5708    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5709    cx.assert_editor_state("{ˇa b");
 5710
 5711    // Don't autoclose if `close` is false for the bracket pair
 5712    cx.set_state("ˇ");
 5713    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5714    cx.assert_editor_state("");
 5715
 5716    // Surround with brackets if text is selected
 5717    cx.set_state("«aˇ» b");
 5718    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5719    cx.assert_editor_state("{«aˇ»} b");
 5720
 5721    // Autclose pair where the start and end characters are the same
 5722    cx.set_state("");
 5723    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5724    cx.assert_editor_state("a\"ˇ\"");
 5725    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5726    cx.assert_editor_state("a\"\"ˇ");
 5727
 5728    // Don't autoclose pair if autoclose is disabled
 5729    cx.set_state("ˇ");
 5730    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5731    cx.assert_editor_state("");
 5732
 5733    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5734    cx.set_state("«aˇ» b");
 5735    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5736    cx.assert_editor_state("<«aˇ»> b");
 5737}
 5738
 5739#[gpui::test]
 5740async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5741    init_test(cx, |settings| {
 5742        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5743    });
 5744
 5745    let mut cx = EditorTestContext::new(cx).await;
 5746
 5747    let language = Arc::new(Language::new(
 5748        LanguageConfig {
 5749            brackets: BracketPairConfig {
 5750                pairs: vec![
 5751                    BracketPair {
 5752                        start: "{".to_string(),
 5753                        end: "}".to_string(),
 5754                        close: true,
 5755                        surround: true,
 5756                        newline: true,
 5757                    },
 5758                    BracketPair {
 5759                        start: "(".to_string(),
 5760                        end: ")".to_string(),
 5761                        close: true,
 5762                        surround: true,
 5763                        newline: true,
 5764                    },
 5765                    BracketPair {
 5766                        start: "[".to_string(),
 5767                        end: "]".to_string(),
 5768                        close: false,
 5769                        surround: false,
 5770                        newline: true,
 5771                    },
 5772                ],
 5773                ..Default::default()
 5774            },
 5775            autoclose_before: "})]".to_string(),
 5776            ..Default::default()
 5777        },
 5778        Some(tree_sitter_rust::LANGUAGE.into()),
 5779    ));
 5780
 5781    cx.language_registry().add(language.clone());
 5782    cx.update_buffer(|buffer, cx| {
 5783        buffer.set_language(Some(language), cx);
 5784    });
 5785
 5786    cx.set_state(
 5787        &"
 5788            ˇ
 5789            ˇ
 5790            ˇ
 5791        "
 5792        .unindent(),
 5793    );
 5794
 5795    // ensure only matching closing brackets are skipped over
 5796    cx.update_editor(|view, cx| {
 5797        view.handle_input("}", cx);
 5798        view.move_left(&MoveLeft, cx);
 5799        view.handle_input(")", cx);
 5800        view.move_left(&MoveLeft, cx);
 5801    });
 5802    cx.assert_editor_state(
 5803        &"
 5804            ˇ)}
 5805            ˇ)}
 5806            ˇ)}
 5807        "
 5808        .unindent(),
 5809    );
 5810
 5811    // skip-over closing brackets at multiple cursors
 5812    cx.update_editor(|view, cx| {
 5813        view.handle_input(")", cx);
 5814        view.handle_input("}", cx);
 5815    });
 5816    cx.assert_editor_state(
 5817        &"
 5818            )}ˇ
 5819            )}ˇ
 5820            )}ˇ
 5821        "
 5822        .unindent(),
 5823    );
 5824
 5825    // ignore non-close brackets
 5826    cx.update_editor(|view, cx| {
 5827        view.handle_input("]", cx);
 5828        view.move_left(&MoveLeft, cx);
 5829        view.handle_input("]", cx);
 5830    });
 5831    cx.assert_editor_state(
 5832        &"
 5833            )}]ˇ]
 5834            )}]ˇ]
 5835            )}]ˇ]
 5836        "
 5837        .unindent(),
 5838    );
 5839}
 5840
 5841#[gpui::test]
 5842async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5843    init_test(cx, |_| {});
 5844
 5845    let mut cx = EditorTestContext::new(cx).await;
 5846
 5847    let html_language = Arc::new(
 5848        Language::new(
 5849            LanguageConfig {
 5850                name: "HTML".into(),
 5851                brackets: BracketPairConfig {
 5852                    pairs: vec![
 5853                        BracketPair {
 5854                            start: "<".into(),
 5855                            end: ">".into(),
 5856                            close: true,
 5857                            ..Default::default()
 5858                        },
 5859                        BracketPair {
 5860                            start: "{".into(),
 5861                            end: "}".into(),
 5862                            close: true,
 5863                            ..Default::default()
 5864                        },
 5865                        BracketPair {
 5866                            start: "(".into(),
 5867                            end: ")".into(),
 5868                            close: true,
 5869                            ..Default::default()
 5870                        },
 5871                    ],
 5872                    ..Default::default()
 5873                },
 5874                autoclose_before: "})]>".into(),
 5875                ..Default::default()
 5876            },
 5877            Some(tree_sitter_html::language()),
 5878        )
 5879        .with_injection_query(
 5880            r#"
 5881            (script_element
 5882                (raw_text) @content
 5883                (#set! "language" "javascript"))
 5884            "#,
 5885        )
 5886        .unwrap(),
 5887    );
 5888
 5889    let javascript_language = Arc::new(Language::new(
 5890        LanguageConfig {
 5891            name: "JavaScript".into(),
 5892            brackets: BracketPairConfig {
 5893                pairs: vec![
 5894                    BracketPair {
 5895                        start: "/*".into(),
 5896                        end: " */".into(),
 5897                        close: true,
 5898                        ..Default::default()
 5899                    },
 5900                    BracketPair {
 5901                        start: "{".into(),
 5902                        end: "}".into(),
 5903                        close: true,
 5904                        ..Default::default()
 5905                    },
 5906                    BracketPair {
 5907                        start: "(".into(),
 5908                        end: ")".into(),
 5909                        close: true,
 5910                        ..Default::default()
 5911                    },
 5912                ],
 5913                ..Default::default()
 5914            },
 5915            autoclose_before: "})]>".into(),
 5916            ..Default::default()
 5917        },
 5918        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5919    ));
 5920
 5921    cx.language_registry().add(html_language.clone());
 5922    cx.language_registry().add(javascript_language.clone());
 5923
 5924    cx.update_buffer(|buffer, cx| {
 5925        buffer.set_language(Some(html_language), cx);
 5926    });
 5927
 5928    cx.set_state(
 5929        &r#"
 5930            <body>ˇ
 5931                <script>
 5932                    var x = 1;ˇ
 5933                </script>
 5934            </body>ˇ
 5935        "#
 5936        .unindent(),
 5937    );
 5938
 5939    // Precondition: different languages are active at different locations.
 5940    cx.update_editor(|editor, cx| {
 5941        let snapshot = editor.snapshot(cx);
 5942        let cursors = editor.selections.ranges::<usize>(cx);
 5943        let languages = cursors
 5944            .iter()
 5945            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5946            .collect::<Vec<_>>();
 5947        assert_eq!(
 5948            languages,
 5949            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5950        );
 5951    });
 5952
 5953    // Angle brackets autoclose in HTML, but not JavaScript.
 5954    cx.update_editor(|editor, cx| {
 5955        editor.handle_input("<", cx);
 5956        editor.handle_input("a", cx);
 5957    });
 5958    cx.assert_editor_state(
 5959        &r#"
 5960            <body><aˇ>
 5961                <script>
 5962                    var x = 1;<aˇ
 5963                </script>
 5964            </body><aˇ>
 5965        "#
 5966        .unindent(),
 5967    );
 5968
 5969    // Curly braces and parens autoclose in both HTML and JavaScript.
 5970    cx.update_editor(|editor, cx| {
 5971        editor.handle_input(" b=", cx);
 5972        editor.handle_input("{", cx);
 5973        editor.handle_input("c", cx);
 5974        editor.handle_input("(", cx);
 5975    });
 5976    cx.assert_editor_state(
 5977        &r#"
 5978            <body><a b={c(ˇ)}>
 5979                <script>
 5980                    var x = 1;<a b={c(ˇ)}
 5981                </script>
 5982            </body><a b={c(ˇ)}>
 5983        "#
 5984        .unindent(),
 5985    );
 5986
 5987    // Brackets that were already autoclosed are skipped.
 5988    cx.update_editor(|editor, cx| {
 5989        editor.handle_input(")", cx);
 5990        editor.handle_input("d", cx);
 5991        editor.handle_input("}", cx);
 5992    });
 5993    cx.assert_editor_state(
 5994        &r#"
 5995            <body><a b={c()d}ˇ>
 5996                <script>
 5997                    var x = 1;<a b={c()d}ˇ
 5998                </script>
 5999            </body><a b={c()d}ˇ>
 6000        "#
 6001        .unindent(),
 6002    );
 6003    cx.update_editor(|editor, cx| {
 6004        editor.handle_input(">", cx);
 6005    });
 6006    cx.assert_editor_state(
 6007        &r#"
 6008            <body><a b={c()d}>ˇ
 6009                <script>
 6010                    var x = 1;<a b={c()d}>ˇ
 6011                </script>
 6012            </body><a b={c()d}>ˇ
 6013        "#
 6014        .unindent(),
 6015    );
 6016
 6017    // Reset
 6018    cx.set_state(
 6019        &r#"
 6020            <body>ˇ
 6021                <script>
 6022                    var x = 1;ˇ
 6023                </script>
 6024            </body>ˇ
 6025        "#
 6026        .unindent(),
 6027    );
 6028
 6029    cx.update_editor(|editor, cx| {
 6030        editor.handle_input("<", cx);
 6031    });
 6032    cx.assert_editor_state(
 6033        &r#"
 6034            <body><ˇ>
 6035                <script>
 6036                    var x = 1;<ˇ
 6037                </script>
 6038            </body><ˇ>
 6039        "#
 6040        .unindent(),
 6041    );
 6042
 6043    // When backspacing, the closing angle brackets are removed.
 6044    cx.update_editor(|editor, cx| {
 6045        editor.backspace(&Backspace, cx);
 6046    });
 6047    cx.assert_editor_state(
 6048        &r#"
 6049            <body>ˇ
 6050                <script>
 6051                    var x = 1;ˇ
 6052                </script>
 6053            </body>ˇ
 6054        "#
 6055        .unindent(),
 6056    );
 6057
 6058    // Block comments autoclose in JavaScript, but not HTML.
 6059    cx.update_editor(|editor, cx| {
 6060        editor.handle_input("/", cx);
 6061        editor.handle_input("*", cx);
 6062    });
 6063    cx.assert_editor_state(
 6064        &r#"
 6065            <body>/*ˇ
 6066                <script>
 6067                    var x = 1;/*ˇ */
 6068                </script>
 6069            </body>/*ˇ
 6070        "#
 6071        .unindent(),
 6072    );
 6073}
 6074
 6075#[gpui::test]
 6076async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6077    init_test(cx, |_| {});
 6078
 6079    let mut cx = EditorTestContext::new(cx).await;
 6080
 6081    let rust_language = Arc::new(
 6082        Language::new(
 6083            LanguageConfig {
 6084                name: "Rust".into(),
 6085                brackets: serde_json::from_value(json!([
 6086                    { "start": "{", "end": "}", "close": true, "newline": true },
 6087                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6088                ]))
 6089                .unwrap(),
 6090                autoclose_before: "})]>".into(),
 6091                ..Default::default()
 6092            },
 6093            Some(tree_sitter_rust::LANGUAGE.into()),
 6094        )
 6095        .with_override_query("(string_literal) @string")
 6096        .unwrap(),
 6097    );
 6098
 6099    cx.language_registry().add(rust_language.clone());
 6100    cx.update_buffer(|buffer, cx| {
 6101        buffer.set_language(Some(rust_language), cx);
 6102    });
 6103
 6104    cx.set_state(
 6105        &r#"
 6106            let x = ˇ
 6107        "#
 6108        .unindent(),
 6109    );
 6110
 6111    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6112    cx.update_editor(|editor, cx| {
 6113        editor.handle_input("\"", cx);
 6114    });
 6115    cx.assert_editor_state(
 6116        &r#"
 6117            let x = "ˇ"
 6118        "#
 6119        .unindent(),
 6120    );
 6121
 6122    // Inserting another quotation mark. The cursor moves across the existing
 6123    // automatically-inserted quotation mark.
 6124    cx.update_editor(|editor, cx| {
 6125        editor.handle_input("\"", cx);
 6126    });
 6127    cx.assert_editor_state(
 6128        &r#"
 6129            let x = ""ˇ
 6130        "#
 6131        .unindent(),
 6132    );
 6133
 6134    // Reset
 6135    cx.set_state(
 6136        &r#"
 6137            let x = ˇ
 6138        "#
 6139        .unindent(),
 6140    );
 6141
 6142    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6143    cx.update_editor(|editor, cx| {
 6144        editor.handle_input("\"", cx);
 6145        editor.handle_input(" ", cx);
 6146        editor.move_left(&Default::default(), cx);
 6147        editor.handle_input("\\", cx);
 6148        editor.handle_input("\"", cx);
 6149    });
 6150    cx.assert_editor_state(
 6151        &r#"
 6152            let x = "\"ˇ "
 6153        "#
 6154        .unindent(),
 6155    );
 6156
 6157    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6158    // mark. Nothing is inserted.
 6159    cx.update_editor(|editor, cx| {
 6160        editor.move_right(&Default::default(), cx);
 6161        editor.handle_input("\"", cx);
 6162    });
 6163    cx.assert_editor_state(
 6164        &r#"
 6165            let x = "\" "ˇ
 6166        "#
 6167        .unindent(),
 6168    );
 6169}
 6170
 6171#[gpui::test]
 6172async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6173    init_test(cx, |_| {});
 6174
 6175    let language = Arc::new(Language::new(
 6176        LanguageConfig {
 6177            brackets: BracketPairConfig {
 6178                pairs: vec![
 6179                    BracketPair {
 6180                        start: "{".to_string(),
 6181                        end: "}".to_string(),
 6182                        close: true,
 6183                        surround: true,
 6184                        newline: true,
 6185                    },
 6186                    BracketPair {
 6187                        start: "/* ".to_string(),
 6188                        end: "*/".to_string(),
 6189                        close: true,
 6190                        surround: true,
 6191                        ..Default::default()
 6192                    },
 6193                ],
 6194                ..Default::default()
 6195            },
 6196            ..Default::default()
 6197        },
 6198        Some(tree_sitter_rust::LANGUAGE.into()),
 6199    ));
 6200
 6201    let text = r#"
 6202        a
 6203        b
 6204        c
 6205    "#
 6206    .unindent();
 6207
 6208    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6209    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6210    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6211    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6212        .await;
 6213
 6214    view.update(cx, |view, cx| {
 6215        view.change_selections(None, cx, |s| {
 6216            s.select_display_ranges([
 6217                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6218                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6219                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6220            ])
 6221        });
 6222
 6223        view.handle_input("{", cx);
 6224        view.handle_input("{", cx);
 6225        view.handle_input("{", cx);
 6226        assert_eq!(
 6227            view.text(cx),
 6228            "
 6229                {{{a}}}
 6230                {{{b}}}
 6231                {{{c}}}
 6232            "
 6233            .unindent()
 6234        );
 6235        assert_eq!(
 6236            view.selections.display_ranges(cx),
 6237            [
 6238                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6239                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6240                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6241            ]
 6242        );
 6243
 6244        view.undo(&Undo, cx);
 6245        view.undo(&Undo, cx);
 6246        view.undo(&Undo, cx);
 6247        assert_eq!(
 6248            view.text(cx),
 6249            "
 6250                a
 6251                b
 6252                c
 6253            "
 6254            .unindent()
 6255        );
 6256        assert_eq!(
 6257            view.selections.display_ranges(cx),
 6258            [
 6259                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6260                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6261                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6262            ]
 6263        );
 6264
 6265        // Ensure inserting the first character of a multi-byte bracket pair
 6266        // doesn't surround the selections with the bracket.
 6267        view.handle_input("/", cx);
 6268        assert_eq!(
 6269            view.text(cx),
 6270            "
 6271                /
 6272                /
 6273                /
 6274            "
 6275            .unindent()
 6276        );
 6277        assert_eq!(
 6278            view.selections.display_ranges(cx),
 6279            [
 6280                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6281                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6282                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6283            ]
 6284        );
 6285
 6286        view.undo(&Undo, 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), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6300                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6301                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6302            ]
 6303        );
 6304
 6305        // Ensure inserting the last character of a multi-byte bracket pair
 6306        // doesn't surround the selections with the bracket.
 6307        view.handle_input("*", cx);
 6308        assert_eq!(
 6309            view.text(cx),
 6310            "
 6311                *
 6312                *
 6313                *
 6314            "
 6315            .unindent()
 6316        );
 6317        assert_eq!(
 6318            view.selections.display_ranges(cx),
 6319            [
 6320                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6321                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6322                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6323            ]
 6324        );
 6325    });
 6326}
 6327
 6328#[gpui::test]
 6329async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6330    init_test(cx, |_| {});
 6331
 6332    let language = Arc::new(Language::new(
 6333        LanguageConfig {
 6334            brackets: BracketPairConfig {
 6335                pairs: vec![BracketPair {
 6336                    start: "{".to_string(),
 6337                    end: "}".to_string(),
 6338                    close: true,
 6339                    surround: true,
 6340                    newline: true,
 6341                }],
 6342                ..Default::default()
 6343            },
 6344            autoclose_before: "}".to_string(),
 6345            ..Default::default()
 6346        },
 6347        Some(tree_sitter_rust::LANGUAGE.into()),
 6348    ));
 6349
 6350    let text = r#"
 6351        a
 6352        b
 6353        c
 6354    "#
 6355    .unindent();
 6356
 6357    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6358    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6359    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6360    editor
 6361        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6362        .await;
 6363
 6364    editor.update(cx, |editor, cx| {
 6365        editor.change_selections(None, cx, |s| {
 6366            s.select_ranges([
 6367                Point::new(0, 1)..Point::new(0, 1),
 6368                Point::new(1, 1)..Point::new(1, 1),
 6369                Point::new(2, 1)..Point::new(2, 1),
 6370            ])
 6371        });
 6372
 6373        editor.handle_input("{", cx);
 6374        editor.handle_input("{", cx);
 6375        editor.handle_input("_", cx);
 6376        assert_eq!(
 6377            editor.text(cx),
 6378            "
 6379                a{{_}}
 6380                b{{_}}
 6381                c{{_}}
 6382            "
 6383            .unindent()
 6384        );
 6385        assert_eq!(
 6386            editor.selections.ranges::<Point>(cx),
 6387            [
 6388                Point::new(0, 4)..Point::new(0, 4),
 6389                Point::new(1, 4)..Point::new(1, 4),
 6390                Point::new(2, 4)..Point::new(2, 4)
 6391            ]
 6392        );
 6393
 6394        editor.backspace(&Default::default(), cx);
 6395        editor.backspace(&Default::default(), cx);
 6396        assert_eq!(
 6397            editor.text(cx),
 6398            "
 6399                a{}
 6400                b{}
 6401                c{}
 6402            "
 6403            .unindent()
 6404        );
 6405        assert_eq!(
 6406            editor.selections.ranges::<Point>(cx),
 6407            [
 6408                Point::new(0, 2)..Point::new(0, 2),
 6409                Point::new(1, 2)..Point::new(1, 2),
 6410                Point::new(2, 2)..Point::new(2, 2)
 6411            ]
 6412        );
 6413
 6414        editor.delete_to_previous_word_start(&Default::default(), cx);
 6415        assert_eq!(
 6416            editor.text(cx),
 6417            "
 6418                a
 6419                b
 6420                c
 6421            "
 6422            .unindent()
 6423        );
 6424        assert_eq!(
 6425            editor.selections.ranges::<Point>(cx),
 6426            [
 6427                Point::new(0, 1)..Point::new(0, 1),
 6428                Point::new(1, 1)..Point::new(1, 1),
 6429                Point::new(2, 1)..Point::new(2, 1)
 6430            ]
 6431        );
 6432    });
 6433}
 6434
 6435#[gpui::test]
 6436async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6437    init_test(cx, |settings| {
 6438        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6439    });
 6440
 6441    let mut cx = EditorTestContext::new(cx).await;
 6442
 6443    let language = Arc::new(Language::new(
 6444        LanguageConfig {
 6445            brackets: BracketPairConfig {
 6446                pairs: vec![
 6447                    BracketPair {
 6448                        start: "{".to_string(),
 6449                        end: "}".to_string(),
 6450                        close: true,
 6451                        surround: true,
 6452                        newline: true,
 6453                    },
 6454                    BracketPair {
 6455                        start: "(".to_string(),
 6456                        end: ")".to_string(),
 6457                        close: true,
 6458                        surround: true,
 6459                        newline: true,
 6460                    },
 6461                    BracketPair {
 6462                        start: "[".to_string(),
 6463                        end: "]".to_string(),
 6464                        close: false,
 6465                        surround: true,
 6466                        newline: true,
 6467                    },
 6468                ],
 6469                ..Default::default()
 6470            },
 6471            autoclose_before: "})]".to_string(),
 6472            ..Default::default()
 6473        },
 6474        Some(tree_sitter_rust::LANGUAGE.into()),
 6475    ));
 6476
 6477    cx.language_registry().add(language.clone());
 6478    cx.update_buffer(|buffer, cx| {
 6479        buffer.set_language(Some(language), cx);
 6480    });
 6481
 6482    cx.set_state(
 6483        &"
 6484            {(ˇ)}
 6485            [[ˇ]]
 6486            {(ˇ)}
 6487        "
 6488        .unindent(),
 6489    );
 6490
 6491    cx.update_editor(|view, cx| {
 6492        view.backspace(&Default::default(), cx);
 6493        view.backspace(&Default::default(), cx);
 6494    });
 6495
 6496    cx.assert_editor_state(
 6497        &"
 6498            ˇ
 6499            ˇ]]
 6500            ˇ
 6501        "
 6502        .unindent(),
 6503    );
 6504
 6505    cx.update_editor(|view, cx| {
 6506        view.handle_input("{", cx);
 6507        view.handle_input("{", cx);
 6508        view.move_right(&MoveRight, cx);
 6509        view.move_right(&MoveRight, cx);
 6510        view.move_left(&MoveLeft, cx);
 6511        view.move_left(&MoveLeft, cx);
 6512        view.backspace(&Default::default(), cx);
 6513    });
 6514
 6515    cx.assert_editor_state(
 6516        &"
 6517            {ˇ}
 6518            {ˇ}]]
 6519            {ˇ}
 6520        "
 6521        .unindent(),
 6522    );
 6523
 6524    cx.update_editor(|view, cx| {
 6525        view.backspace(&Default::default(), cx);
 6526    });
 6527
 6528    cx.assert_editor_state(
 6529        &"
 6530            ˇ
 6531            ˇ]]
 6532            ˇ
 6533        "
 6534        .unindent(),
 6535    );
 6536}
 6537
 6538#[gpui::test]
 6539async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6540    init_test(cx, |_| {});
 6541
 6542    let language = Arc::new(Language::new(
 6543        LanguageConfig::default(),
 6544        Some(tree_sitter_rust::LANGUAGE.into()),
 6545    ));
 6546
 6547    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6548    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6549    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6550    editor
 6551        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6552        .await;
 6553
 6554    editor.update(cx, |editor, cx| {
 6555        editor.set_auto_replace_emoji_shortcode(true);
 6556
 6557        editor.handle_input("Hello ", cx);
 6558        editor.handle_input(":wave", cx);
 6559        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6560
 6561        editor.handle_input(":", cx);
 6562        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6563
 6564        editor.handle_input(" :smile", cx);
 6565        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6566
 6567        editor.handle_input(":", cx);
 6568        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6569
 6570        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6571        editor.handle_input(":wave", cx);
 6572        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6573
 6574        editor.handle_input(":", cx);
 6575        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6576
 6577        editor.handle_input(":1", cx);
 6578        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6579
 6580        editor.handle_input(":", cx);
 6581        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6582
 6583        // Ensure shortcode does not get replaced when it is part of a word
 6584        editor.handle_input(" Test:wave", cx);
 6585        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6586
 6587        editor.handle_input(":", cx);
 6588        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6589
 6590        editor.set_auto_replace_emoji_shortcode(false);
 6591
 6592        // Ensure shortcode does not get replaced when auto replace is off
 6593        editor.handle_input(" :wave", cx);
 6594        assert_eq!(
 6595            editor.text(cx),
 6596            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6597        );
 6598
 6599        editor.handle_input(":", cx);
 6600        assert_eq!(
 6601            editor.text(cx),
 6602            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6603        );
 6604    });
 6605}
 6606
 6607#[gpui::test]
 6608async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6609    init_test(cx, |_| {});
 6610
 6611    let (text, insertion_ranges) = marked_text_ranges(
 6612        indoc! {"
 6613            a.ˇ b
 6614            a.ˇ b
 6615            a.ˇ b
 6616        "},
 6617        false,
 6618    );
 6619
 6620    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6621    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6622
 6623    editor.update(cx, |editor, cx| {
 6624        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6625
 6626        editor
 6627            .insert_snippet(&insertion_ranges, snippet, cx)
 6628            .unwrap();
 6629
 6630        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6631            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6632            assert_eq!(editor.text(cx), expected_text);
 6633            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6634        }
 6635
 6636        assert(
 6637            editor,
 6638            cx,
 6639            indoc! {"
 6640                a.f(«one», two, «three») b
 6641                a.f(«one», two, «three») b
 6642                a.f(«one», two, «three») b
 6643            "},
 6644        );
 6645
 6646        // Can't move earlier than the first tab stop
 6647        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6648        assert(
 6649            editor,
 6650            cx,
 6651            indoc! {"
 6652                a.f(«one», two, «three») b
 6653                a.f(«one», two, «three») b
 6654                a.f(«one», two, «three») b
 6655            "},
 6656        );
 6657
 6658        assert!(editor.move_to_next_snippet_tabstop(cx));
 6659        assert(
 6660            editor,
 6661            cx,
 6662            indoc! {"
 6663                a.f(one, «two», three) b
 6664                a.f(one, «two», three) b
 6665                a.f(one, «two», three) b
 6666            "},
 6667        );
 6668
 6669        editor.move_to_prev_snippet_tabstop(cx);
 6670        assert(
 6671            editor,
 6672            cx,
 6673            indoc! {"
 6674                a.f(«one», two, «three») b
 6675                a.f(«one», two, «three») b
 6676                a.f(«one», two, «three») b
 6677            "},
 6678        );
 6679
 6680        assert!(editor.move_to_next_snippet_tabstop(cx));
 6681        assert(
 6682            editor,
 6683            cx,
 6684            indoc! {"
 6685                a.f(one, «two», three) b
 6686                a.f(one, «two», three) b
 6687                a.f(one, «two», three) b
 6688            "},
 6689        );
 6690        assert!(editor.move_to_next_snippet_tabstop(cx));
 6691        assert(
 6692            editor,
 6693            cx,
 6694            indoc! {"
 6695                a.f(one, two, three)ˇ b
 6696                a.f(one, two, three)ˇ b
 6697                a.f(one, two, three)ˇ b
 6698            "},
 6699        );
 6700
 6701        // As soon as the last tab stop is reached, snippet state is gone
 6702        editor.move_to_prev_snippet_tabstop(cx);
 6703        assert(
 6704            editor,
 6705            cx,
 6706            indoc! {"
 6707                a.f(one, two, three)ˇ b
 6708                a.f(one, two, three)ˇ b
 6709                a.f(one, two, three)ˇ b
 6710            "},
 6711        );
 6712    });
 6713}
 6714
 6715#[gpui::test]
 6716async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6717    init_test(cx, |_| {});
 6718
 6719    let fs = FakeFs::new(cx.executor());
 6720    fs.insert_file("/file.rs", Default::default()).await;
 6721
 6722    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6723
 6724    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6725    language_registry.add(rust_lang());
 6726    let mut fake_servers = language_registry.register_fake_lsp(
 6727        "Rust",
 6728        FakeLspAdapter {
 6729            capabilities: lsp::ServerCapabilities {
 6730                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6731                ..Default::default()
 6732            },
 6733            ..Default::default()
 6734        },
 6735    );
 6736
 6737    let buffer = project
 6738        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6739        .await
 6740        .unwrap();
 6741
 6742    cx.executor().start_waiting();
 6743    let fake_server = fake_servers.next().await.unwrap();
 6744
 6745    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6746    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6747    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6748    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6749
 6750    let save = editor
 6751        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6752        .unwrap();
 6753    fake_server
 6754        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6755            assert_eq!(
 6756                params.text_document.uri,
 6757                lsp::Url::from_file_path("/file.rs").unwrap()
 6758            );
 6759            assert_eq!(params.options.tab_size, 4);
 6760            Ok(Some(vec![lsp::TextEdit::new(
 6761                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6762                ", ".to_string(),
 6763            )]))
 6764        })
 6765        .next()
 6766        .await;
 6767    cx.executor().start_waiting();
 6768    save.await;
 6769
 6770    assert_eq!(
 6771        editor.update(cx, |editor, cx| editor.text(cx)),
 6772        "one, two\nthree\n"
 6773    );
 6774    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6775
 6776    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6777    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6778
 6779    // Ensure we can still save even if formatting hangs.
 6780    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6781        assert_eq!(
 6782            params.text_document.uri,
 6783            lsp::Url::from_file_path("/file.rs").unwrap()
 6784        );
 6785        futures::future::pending::<()>().await;
 6786        unreachable!()
 6787    });
 6788    let save = editor
 6789        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6790        .unwrap();
 6791    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6792    cx.executor().start_waiting();
 6793    save.await;
 6794    assert_eq!(
 6795        editor.update(cx, |editor, cx| editor.text(cx)),
 6796        "one\ntwo\nthree\n"
 6797    );
 6798    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6799
 6800    // For non-dirty buffer, no formatting request should be sent
 6801    let save = editor
 6802        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6803        .unwrap();
 6804    let _pending_format_request = fake_server
 6805        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6806            panic!("Should not be invoked on non-dirty buffer");
 6807        })
 6808        .next();
 6809    cx.executor().start_waiting();
 6810    save.await;
 6811
 6812    // Set rust language override and assert overridden tabsize is sent to language server
 6813    update_test_language_settings(cx, |settings| {
 6814        settings.languages.insert(
 6815            "Rust".into(),
 6816            LanguageSettingsContent {
 6817                tab_size: NonZeroU32::new(8),
 6818                ..Default::default()
 6819            },
 6820        );
 6821    });
 6822
 6823    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6824    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6825    let save = editor
 6826        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6827        .unwrap();
 6828    fake_server
 6829        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6830            assert_eq!(
 6831                params.text_document.uri,
 6832                lsp::Url::from_file_path("/file.rs").unwrap()
 6833            );
 6834            assert_eq!(params.options.tab_size, 8);
 6835            Ok(Some(vec![]))
 6836        })
 6837        .next()
 6838        .await;
 6839    cx.executor().start_waiting();
 6840    save.await;
 6841}
 6842
 6843#[gpui::test]
 6844async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6845    init_test(cx, |_| {});
 6846
 6847    let cols = 4;
 6848    let rows = 10;
 6849    let sample_text_1 = sample_text(rows, cols, 'a');
 6850    assert_eq!(
 6851        sample_text_1,
 6852        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6853    );
 6854    let sample_text_2 = sample_text(rows, cols, 'l');
 6855    assert_eq!(
 6856        sample_text_2,
 6857        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6858    );
 6859    let sample_text_3 = sample_text(rows, cols, 'v');
 6860    assert_eq!(
 6861        sample_text_3,
 6862        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6863    );
 6864
 6865    let fs = FakeFs::new(cx.executor());
 6866    fs.insert_tree(
 6867        "/a",
 6868        json!({
 6869            "main.rs": sample_text_1,
 6870            "other.rs": sample_text_2,
 6871            "lib.rs": sample_text_3,
 6872        }),
 6873    )
 6874    .await;
 6875
 6876    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6877    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6878    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6879
 6880    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6881    language_registry.add(rust_lang());
 6882    let mut fake_servers = language_registry.register_fake_lsp(
 6883        "Rust",
 6884        FakeLspAdapter {
 6885            capabilities: lsp::ServerCapabilities {
 6886                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6887                ..Default::default()
 6888            },
 6889            ..Default::default()
 6890        },
 6891    );
 6892
 6893    let worktree = project.update(cx, |project, cx| {
 6894        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6895        assert_eq!(worktrees.len(), 1);
 6896        worktrees.pop().unwrap()
 6897    });
 6898    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6899
 6900    let buffer_1 = project
 6901        .update(cx, |project, cx| {
 6902            project.open_buffer((worktree_id, "main.rs"), cx)
 6903        })
 6904        .await
 6905        .unwrap();
 6906    let buffer_2 = project
 6907        .update(cx, |project, cx| {
 6908            project.open_buffer((worktree_id, "other.rs"), cx)
 6909        })
 6910        .await
 6911        .unwrap();
 6912    let buffer_3 = project
 6913        .update(cx, |project, cx| {
 6914            project.open_buffer((worktree_id, "lib.rs"), cx)
 6915        })
 6916        .await
 6917        .unwrap();
 6918
 6919    let multi_buffer = cx.new_model(|cx| {
 6920        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6921        multi_buffer.push_excerpts(
 6922            buffer_1.clone(),
 6923            [
 6924                ExcerptRange {
 6925                    context: Point::new(0, 0)..Point::new(3, 0),
 6926                    primary: None,
 6927                },
 6928                ExcerptRange {
 6929                    context: Point::new(5, 0)..Point::new(7, 0),
 6930                    primary: None,
 6931                },
 6932                ExcerptRange {
 6933                    context: Point::new(9, 0)..Point::new(10, 4),
 6934                    primary: None,
 6935                },
 6936            ],
 6937            cx,
 6938        );
 6939        multi_buffer.push_excerpts(
 6940            buffer_2.clone(),
 6941            [
 6942                ExcerptRange {
 6943                    context: Point::new(0, 0)..Point::new(3, 0),
 6944                    primary: None,
 6945                },
 6946                ExcerptRange {
 6947                    context: Point::new(5, 0)..Point::new(7, 0),
 6948                    primary: None,
 6949                },
 6950                ExcerptRange {
 6951                    context: Point::new(9, 0)..Point::new(10, 4),
 6952                    primary: None,
 6953                },
 6954            ],
 6955            cx,
 6956        );
 6957        multi_buffer.push_excerpts(
 6958            buffer_3.clone(),
 6959            [
 6960                ExcerptRange {
 6961                    context: Point::new(0, 0)..Point::new(3, 0),
 6962                    primary: None,
 6963                },
 6964                ExcerptRange {
 6965                    context: Point::new(5, 0)..Point::new(7, 0),
 6966                    primary: None,
 6967                },
 6968                ExcerptRange {
 6969                    context: Point::new(9, 0)..Point::new(10, 4),
 6970                    primary: None,
 6971                },
 6972            ],
 6973            cx,
 6974        );
 6975        multi_buffer
 6976    });
 6977    let multi_buffer_editor = cx.new_view(|cx| {
 6978        Editor::new(
 6979            EditorMode::Full,
 6980            multi_buffer,
 6981            Some(project.clone()),
 6982            true,
 6983            cx,
 6984        )
 6985    });
 6986
 6987    multi_buffer_editor.update(cx, |editor, cx| {
 6988        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6989        editor.insert("|one|two|three|", cx);
 6990    });
 6991    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6992    multi_buffer_editor.update(cx, |editor, cx| {
 6993        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6994            s.select_ranges(Some(60..70))
 6995        });
 6996        editor.insert("|four|five|six|", cx);
 6997    });
 6998    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6999
 7000    // First two buffers should be edited, but not the third one.
 7001    assert_eq!(
 7002        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7003        "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}",
 7004    );
 7005    buffer_1.update(cx, |buffer, _| {
 7006        assert!(buffer.is_dirty());
 7007        assert_eq!(
 7008            buffer.text(),
 7009            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7010        )
 7011    });
 7012    buffer_2.update(cx, |buffer, _| {
 7013        assert!(buffer.is_dirty());
 7014        assert_eq!(
 7015            buffer.text(),
 7016            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7017        )
 7018    });
 7019    buffer_3.update(cx, |buffer, _| {
 7020        assert!(!buffer.is_dirty());
 7021        assert_eq!(buffer.text(), sample_text_3,)
 7022    });
 7023
 7024    cx.executor().start_waiting();
 7025    let save = multi_buffer_editor
 7026        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7027        .unwrap();
 7028
 7029    let fake_server = fake_servers.next().await.unwrap();
 7030    fake_server
 7031        .server
 7032        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7033            Ok(Some(vec![lsp::TextEdit::new(
 7034                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7035                format!("[{} formatted]", params.text_document.uri),
 7036            )]))
 7037        })
 7038        .detach();
 7039    save.await;
 7040
 7041    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7042    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7043    assert_eq!(
 7044        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7045        "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}",
 7046    );
 7047    buffer_1.update(cx, |buffer, _| {
 7048        assert!(!buffer.is_dirty());
 7049        assert_eq!(
 7050            buffer.text(),
 7051            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7052        )
 7053    });
 7054    buffer_2.update(cx, |buffer, _| {
 7055        assert!(!buffer.is_dirty());
 7056        assert_eq!(
 7057            buffer.text(),
 7058            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7059        )
 7060    });
 7061    buffer_3.update(cx, |buffer, _| {
 7062        assert!(!buffer.is_dirty());
 7063        assert_eq!(buffer.text(), sample_text_3,)
 7064    });
 7065}
 7066
 7067#[gpui::test]
 7068async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7069    init_test(cx, |_| {});
 7070
 7071    let fs = FakeFs::new(cx.executor());
 7072    fs.insert_file("/file.rs", Default::default()).await;
 7073
 7074    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7075
 7076    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7077    language_registry.add(rust_lang());
 7078    let mut fake_servers = language_registry.register_fake_lsp(
 7079        "Rust",
 7080        FakeLspAdapter {
 7081            capabilities: lsp::ServerCapabilities {
 7082                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7083                ..Default::default()
 7084            },
 7085            ..Default::default()
 7086        },
 7087    );
 7088
 7089    let buffer = project
 7090        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7091        .await
 7092        .unwrap();
 7093
 7094    cx.executor().start_waiting();
 7095    let fake_server = fake_servers.next().await.unwrap();
 7096
 7097    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7098    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7099    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7100    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7101
 7102    let save = editor
 7103        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7104        .unwrap();
 7105    fake_server
 7106        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7107            assert_eq!(
 7108                params.text_document.uri,
 7109                lsp::Url::from_file_path("/file.rs").unwrap()
 7110            );
 7111            assert_eq!(params.options.tab_size, 4);
 7112            Ok(Some(vec![lsp::TextEdit::new(
 7113                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7114                ", ".to_string(),
 7115            )]))
 7116        })
 7117        .next()
 7118        .await;
 7119    cx.executor().start_waiting();
 7120    save.await;
 7121    assert_eq!(
 7122        editor.update(cx, |editor, cx| editor.text(cx)),
 7123        "one, two\nthree\n"
 7124    );
 7125    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7126
 7127    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7128    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7129
 7130    // Ensure we can still save even if formatting hangs.
 7131    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7132        move |params, _| async move {
 7133            assert_eq!(
 7134                params.text_document.uri,
 7135                lsp::Url::from_file_path("/file.rs").unwrap()
 7136            );
 7137            futures::future::pending::<()>().await;
 7138            unreachable!()
 7139        },
 7140    );
 7141    let save = editor
 7142        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7143        .unwrap();
 7144    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7145    cx.executor().start_waiting();
 7146    save.await;
 7147    assert_eq!(
 7148        editor.update(cx, |editor, cx| editor.text(cx)),
 7149        "one\ntwo\nthree\n"
 7150    );
 7151    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7152
 7153    // For non-dirty buffer, no formatting request should be sent
 7154    let save = editor
 7155        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7156        .unwrap();
 7157    let _pending_format_request = fake_server
 7158        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7159            panic!("Should not be invoked on non-dirty buffer");
 7160        })
 7161        .next();
 7162    cx.executor().start_waiting();
 7163    save.await;
 7164
 7165    // Set Rust language override and assert overridden tabsize is sent to language server
 7166    update_test_language_settings(cx, |settings| {
 7167        settings.languages.insert(
 7168            "Rust".into(),
 7169            LanguageSettingsContent {
 7170                tab_size: NonZeroU32::new(8),
 7171                ..Default::default()
 7172            },
 7173        );
 7174    });
 7175
 7176    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7177    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7178    let save = editor
 7179        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7180        .unwrap();
 7181    fake_server
 7182        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7183            assert_eq!(
 7184                params.text_document.uri,
 7185                lsp::Url::from_file_path("/file.rs").unwrap()
 7186            );
 7187            assert_eq!(params.options.tab_size, 8);
 7188            Ok(Some(vec![]))
 7189        })
 7190        .next()
 7191        .await;
 7192    cx.executor().start_waiting();
 7193    save.await;
 7194}
 7195
 7196#[gpui::test]
 7197async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7198    init_test(cx, |settings| {
 7199        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7200            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7201        ))
 7202    });
 7203
 7204    let fs = FakeFs::new(cx.executor());
 7205    fs.insert_file("/file.rs", Default::default()).await;
 7206
 7207    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7208
 7209    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7210    language_registry.add(Arc::new(Language::new(
 7211        LanguageConfig {
 7212            name: "Rust".into(),
 7213            matcher: LanguageMatcher {
 7214                path_suffixes: vec!["rs".to_string()],
 7215                ..Default::default()
 7216            },
 7217            ..LanguageConfig::default()
 7218        },
 7219        Some(tree_sitter_rust::LANGUAGE.into()),
 7220    )));
 7221    update_test_language_settings(cx, |settings| {
 7222        // Enable Prettier formatting for the same buffer, and ensure
 7223        // LSP is called instead of Prettier.
 7224        settings.defaults.prettier = Some(PrettierSettings {
 7225            allowed: true,
 7226            ..PrettierSettings::default()
 7227        });
 7228    });
 7229    let mut fake_servers = language_registry.register_fake_lsp(
 7230        "Rust",
 7231        FakeLspAdapter {
 7232            capabilities: lsp::ServerCapabilities {
 7233                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7234                ..Default::default()
 7235            },
 7236            ..Default::default()
 7237        },
 7238    );
 7239
 7240    let buffer = project
 7241        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7242        .await
 7243        .unwrap();
 7244
 7245    cx.executor().start_waiting();
 7246    let fake_server = fake_servers.next().await.unwrap();
 7247
 7248    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7249    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7250    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7251
 7252    let format = editor
 7253        .update(cx, |editor, cx| {
 7254            editor.perform_format(
 7255                project.clone(),
 7256                FormatTrigger::Manual,
 7257                FormatTarget::Buffer,
 7258                cx,
 7259            )
 7260        })
 7261        .unwrap();
 7262    fake_server
 7263        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7264            assert_eq!(
 7265                params.text_document.uri,
 7266                lsp::Url::from_file_path("/file.rs").unwrap()
 7267            );
 7268            assert_eq!(params.options.tab_size, 4);
 7269            Ok(Some(vec![lsp::TextEdit::new(
 7270                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7271                ", ".to_string(),
 7272            )]))
 7273        })
 7274        .next()
 7275        .await;
 7276    cx.executor().start_waiting();
 7277    format.await;
 7278    assert_eq!(
 7279        editor.update(cx, |editor, cx| editor.text(cx)),
 7280        "one, two\nthree\n"
 7281    );
 7282
 7283    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7284    // Ensure we don't lock if formatting hangs.
 7285    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7286        assert_eq!(
 7287            params.text_document.uri,
 7288            lsp::Url::from_file_path("/file.rs").unwrap()
 7289        );
 7290        futures::future::pending::<()>().await;
 7291        unreachable!()
 7292    });
 7293    let format = editor
 7294        .update(cx, |editor, cx| {
 7295            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7296        })
 7297        .unwrap();
 7298    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7299    cx.executor().start_waiting();
 7300    format.await;
 7301    assert_eq!(
 7302        editor.update(cx, |editor, cx| editor.text(cx)),
 7303        "one\ntwo\nthree\n"
 7304    );
 7305}
 7306
 7307#[gpui::test]
 7308async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7309    init_test(cx, |_| {});
 7310
 7311    let mut cx = EditorLspTestContext::new_rust(
 7312        lsp::ServerCapabilities {
 7313            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7314            ..Default::default()
 7315        },
 7316        cx,
 7317    )
 7318    .await;
 7319
 7320    cx.set_state(indoc! {"
 7321        one.twoˇ
 7322    "});
 7323
 7324    // The format request takes a long time. When it completes, it inserts
 7325    // a newline and an indent before the `.`
 7326    cx.lsp
 7327        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7328            let executor = cx.background_executor().clone();
 7329            async move {
 7330                executor.timer(Duration::from_millis(100)).await;
 7331                Ok(Some(vec![lsp::TextEdit {
 7332                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7333                    new_text: "\n    ".into(),
 7334                }]))
 7335            }
 7336        });
 7337
 7338    // Submit a format request.
 7339    let format_1 = cx
 7340        .update_editor(|editor, cx| editor.format(&Format, cx))
 7341        .unwrap();
 7342    cx.executor().run_until_parked();
 7343
 7344    // Submit a second format request.
 7345    let format_2 = cx
 7346        .update_editor(|editor, cx| editor.format(&Format, cx))
 7347        .unwrap();
 7348    cx.executor().run_until_parked();
 7349
 7350    // Wait for both format requests to complete
 7351    cx.executor().advance_clock(Duration::from_millis(200));
 7352    cx.executor().start_waiting();
 7353    format_1.await.unwrap();
 7354    cx.executor().start_waiting();
 7355    format_2.await.unwrap();
 7356
 7357    // The formatting edits only happens once.
 7358    cx.assert_editor_state(indoc! {"
 7359        one
 7360            .twoˇ
 7361    "});
 7362}
 7363
 7364#[gpui::test]
 7365async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7366    init_test(cx, |settings| {
 7367        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7368    });
 7369
 7370    let mut cx = EditorLspTestContext::new_rust(
 7371        lsp::ServerCapabilities {
 7372            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7373            ..Default::default()
 7374        },
 7375        cx,
 7376    )
 7377    .await;
 7378
 7379    // Set up a buffer white some trailing whitespace and no trailing newline.
 7380    cx.set_state(
 7381        &[
 7382            "one ",   //
 7383            "twoˇ",   //
 7384            "three ", //
 7385            "four",   //
 7386        ]
 7387        .join("\n"),
 7388    );
 7389
 7390    // Submit a format request.
 7391    let format = cx
 7392        .update_editor(|editor, cx| editor.format(&Format, cx))
 7393        .unwrap();
 7394
 7395    // Record which buffer changes have been sent to the language server
 7396    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7397    cx.lsp
 7398        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7399            let buffer_changes = buffer_changes.clone();
 7400            move |params, _| {
 7401                buffer_changes.lock().extend(
 7402                    params
 7403                        .content_changes
 7404                        .into_iter()
 7405                        .map(|e| (e.range.unwrap(), e.text)),
 7406                );
 7407            }
 7408        });
 7409
 7410    // Handle formatting requests to the language server.
 7411    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7412        let buffer_changes = buffer_changes.clone();
 7413        move |_, _| {
 7414            // When formatting is requested, trailing whitespace has already been stripped,
 7415            // and the trailing newline has already been added.
 7416            assert_eq!(
 7417                &buffer_changes.lock()[1..],
 7418                &[
 7419                    (
 7420                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7421                        "".into()
 7422                    ),
 7423                    (
 7424                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7425                        "".into()
 7426                    ),
 7427                    (
 7428                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7429                        "\n".into()
 7430                    ),
 7431                ]
 7432            );
 7433
 7434            // Insert blank lines between each line of the buffer.
 7435            async move {
 7436                Ok(Some(vec![
 7437                    lsp::TextEdit {
 7438                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7439                        new_text: "\n".into(),
 7440                    },
 7441                    lsp::TextEdit {
 7442                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7443                        new_text: "\n".into(),
 7444                    },
 7445                ]))
 7446            }
 7447        }
 7448    });
 7449
 7450    // After formatting the buffer, the trailing whitespace is stripped,
 7451    // a newline is appended, and the edits provided by the language server
 7452    // have been applied.
 7453    format.await.unwrap();
 7454    cx.assert_editor_state(
 7455        &[
 7456            "one",   //
 7457            "",      //
 7458            "twoˇ",  //
 7459            "",      //
 7460            "three", //
 7461            "four",  //
 7462            "",      //
 7463        ]
 7464        .join("\n"),
 7465    );
 7466
 7467    // Undoing the formatting undoes the trailing whitespace removal, the
 7468    // trailing newline, and the LSP edits.
 7469    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7470    cx.assert_editor_state(
 7471        &[
 7472            "one ",   //
 7473            "twoˇ",   //
 7474            "three ", //
 7475            "four",   //
 7476        ]
 7477        .join("\n"),
 7478    );
 7479}
 7480
 7481#[gpui::test]
 7482async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7483    cx: &mut gpui::TestAppContext,
 7484) {
 7485    init_test(cx, |_| {});
 7486
 7487    cx.update(|cx| {
 7488        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7489            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7490                settings.auto_signature_help = Some(true);
 7491            });
 7492        });
 7493    });
 7494
 7495    let mut cx = EditorLspTestContext::new_rust(
 7496        lsp::ServerCapabilities {
 7497            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7498                ..Default::default()
 7499            }),
 7500            ..Default::default()
 7501        },
 7502        cx,
 7503    )
 7504    .await;
 7505
 7506    let language = Language::new(
 7507        LanguageConfig {
 7508            name: "Rust".into(),
 7509            brackets: BracketPairConfig {
 7510                pairs: vec![
 7511                    BracketPair {
 7512                        start: "{".to_string(),
 7513                        end: "}".to_string(),
 7514                        close: true,
 7515                        surround: true,
 7516                        newline: true,
 7517                    },
 7518                    BracketPair {
 7519                        start: "(".to_string(),
 7520                        end: ")".to_string(),
 7521                        close: true,
 7522                        surround: true,
 7523                        newline: true,
 7524                    },
 7525                    BracketPair {
 7526                        start: "/*".to_string(),
 7527                        end: " */".to_string(),
 7528                        close: true,
 7529                        surround: true,
 7530                        newline: true,
 7531                    },
 7532                    BracketPair {
 7533                        start: "[".to_string(),
 7534                        end: "]".to_string(),
 7535                        close: false,
 7536                        surround: false,
 7537                        newline: true,
 7538                    },
 7539                    BracketPair {
 7540                        start: "\"".to_string(),
 7541                        end: "\"".to_string(),
 7542                        close: true,
 7543                        surround: true,
 7544                        newline: false,
 7545                    },
 7546                    BracketPair {
 7547                        start: "<".to_string(),
 7548                        end: ">".to_string(),
 7549                        close: false,
 7550                        surround: true,
 7551                        newline: true,
 7552                    },
 7553                ],
 7554                ..Default::default()
 7555            },
 7556            autoclose_before: "})]".to_string(),
 7557            ..Default::default()
 7558        },
 7559        Some(tree_sitter_rust::LANGUAGE.into()),
 7560    );
 7561    let language = Arc::new(language);
 7562
 7563    cx.language_registry().add(language.clone());
 7564    cx.update_buffer(|buffer, cx| {
 7565        buffer.set_language(Some(language), cx);
 7566    });
 7567
 7568    cx.set_state(
 7569        &r#"
 7570            fn main() {
 7571                sampleˇ
 7572            }
 7573        "#
 7574        .unindent(),
 7575    );
 7576
 7577    cx.update_editor(|view, cx| {
 7578        view.handle_input("(", cx);
 7579    });
 7580    cx.assert_editor_state(
 7581        &"
 7582            fn main() {
 7583                sample(ˇ)
 7584            }
 7585        "
 7586        .unindent(),
 7587    );
 7588
 7589    let mocked_response = lsp::SignatureHelp {
 7590        signatures: vec![lsp::SignatureInformation {
 7591            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7592            documentation: None,
 7593            parameters: Some(vec![
 7594                lsp::ParameterInformation {
 7595                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7596                    documentation: None,
 7597                },
 7598                lsp::ParameterInformation {
 7599                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7600                    documentation: None,
 7601                },
 7602            ]),
 7603            active_parameter: None,
 7604        }],
 7605        active_signature: Some(0),
 7606        active_parameter: Some(0),
 7607    };
 7608    handle_signature_help_request(&mut cx, mocked_response).await;
 7609
 7610    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7611        .await;
 7612
 7613    cx.editor(|editor, _| {
 7614        let signature_help_state = editor.signature_help_state.popover().cloned();
 7615        assert!(signature_help_state.is_some());
 7616        let ParsedMarkdown {
 7617            text, highlights, ..
 7618        } = signature_help_state.unwrap().parsed_content;
 7619        assert_eq!(text, "param1: u8, param2: u8");
 7620        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7621    });
 7622}
 7623
 7624#[gpui::test]
 7625async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7626    init_test(cx, |_| {});
 7627
 7628    cx.update(|cx| {
 7629        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7630            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7631                settings.auto_signature_help = Some(false);
 7632                settings.show_signature_help_after_edits = Some(false);
 7633            });
 7634        });
 7635    });
 7636
 7637    let mut cx = EditorLspTestContext::new_rust(
 7638        lsp::ServerCapabilities {
 7639            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7640                ..Default::default()
 7641            }),
 7642            ..Default::default()
 7643        },
 7644        cx,
 7645    )
 7646    .await;
 7647
 7648    let language = Language::new(
 7649        LanguageConfig {
 7650            name: "Rust".into(),
 7651            brackets: BracketPairConfig {
 7652                pairs: vec![
 7653                    BracketPair {
 7654                        start: "{".to_string(),
 7655                        end: "}".to_string(),
 7656                        close: true,
 7657                        surround: true,
 7658                        newline: true,
 7659                    },
 7660                    BracketPair {
 7661                        start: "(".to_string(),
 7662                        end: ")".to_string(),
 7663                        close: true,
 7664                        surround: true,
 7665                        newline: true,
 7666                    },
 7667                    BracketPair {
 7668                        start: "/*".to_string(),
 7669                        end: " */".to_string(),
 7670                        close: true,
 7671                        surround: true,
 7672                        newline: true,
 7673                    },
 7674                    BracketPair {
 7675                        start: "[".to_string(),
 7676                        end: "]".to_string(),
 7677                        close: false,
 7678                        surround: false,
 7679                        newline: true,
 7680                    },
 7681                    BracketPair {
 7682                        start: "\"".to_string(),
 7683                        end: "\"".to_string(),
 7684                        close: true,
 7685                        surround: true,
 7686                        newline: false,
 7687                    },
 7688                    BracketPair {
 7689                        start: "<".to_string(),
 7690                        end: ">".to_string(),
 7691                        close: false,
 7692                        surround: true,
 7693                        newline: true,
 7694                    },
 7695                ],
 7696                ..Default::default()
 7697            },
 7698            autoclose_before: "})]".to_string(),
 7699            ..Default::default()
 7700        },
 7701        Some(tree_sitter_rust::LANGUAGE.into()),
 7702    );
 7703    let language = Arc::new(language);
 7704
 7705    cx.language_registry().add(language.clone());
 7706    cx.update_buffer(|buffer, cx| {
 7707        buffer.set_language(Some(language), cx);
 7708    });
 7709
 7710    // Ensure that signature_help is not called when no signature help is enabled.
 7711    cx.set_state(
 7712        &r#"
 7713            fn main() {
 7714                sampleˇ
 7715            }
 7716        "#
 7717        .unindent(),
 7718    );
 7719    cx.update_editor(|view, cx| {
 7720        view.handle_input("(", cx);
 7721    });
 7722    cx.assert_editor_state(
 7723        &"
 7724            fn main() {
 7725                sample(ˇ)
 7726            }
 7727        "
 7728        .unindent(),
 7729    );
 7730    cx.editor(|editor, _| {
 7731        assert!(editor.signature_help_state.task().is_none());
 7732    });
 7733
 7734    let mocked_response = lsp::SignatureHelp {
 7735        signatures: vec![lsp::SignatureInformation {
 7736            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7737            documentation: None,
 7738            parameters: Some(vec![
 7739                lsp::ParameterInformation {
 7740                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7741                    documentation: None,
 7742                },
 7743                lsp::ParameterInformation {
 7744                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7745                    documentation: None,
 7746                },
 7747            ]),
 7748            active_parameter: None,
 7749        }],
 7750        active_signature: Some(0),
 7751        active_parameter: Some(0),
 7752    };
 7753
 7754    // Ensure that signature_help is called when enabled afte edits
 7755    cx.update(|cx| {
 7756        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7757            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7758                settings.auto_signature_help = Some(false);
 7759                settings.show_signature_help_after_edits = Some(true);
 7760            });
 7761        });
 7762    });
 7763    cx.set_state(
 7764        &r#"
 7765            fn main() {
 7766                sampleˇ
 7767            }
 7768        "#
 7769        .unindent(),
 7770    );
 7771    cx.update_editor(|view, cx| {
 7772        view.handle_input("(", cx);
 7773    });
 7774    cx.assert_editor_state(
 7775        &"
 7776            fn main() {
 7777                sample(ˇ)
 7778            }
 7779        "
 7780        .unindent(),
 7781    );
 7782    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7783    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7784        .await;
 7785    cx.update_editor(|editor, _| {
 7786        let signature_help_state = editor.signature_help_state.popover().cloned();
 7787        assert!(signature_help_state.is_some());
 7788        let ParsedMarkdown {
 7789            text, highlights, ..
 7790        } = signature_help_state.unwrap().parsed_content;
 7791        assert_eq!(text, "param1: u8, param2: u8");
 7792        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7793        editor.signature_help_state = SignatureHelpState::default();
 7794    });
 7795
 7796    // Ensure that signature_help is called when auto signature help override is enabled
 7797    cx.update(|cx| {
 7798        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7799            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7800                settings.auto_signature_help = Some(true);
 7801                settings.show_signature_help_after_edits = Some(false);
 7802            });
 7803        });
 7804    });
 7805    cx.set_state(
 7806        &r#"
 7807            fn main() {
 7808                sampleˇ
 7809            }
 7810        "#
 7811        .unindent(),
 7812    );
 7813    cx.update_editor(|view, cx| {
 7814        view.handle_input("(", cx);
 7815    });
 7816    cx.assert_editor_state(
 7817        &"
 7818            fn main() {
 7819                sample(ˇ)
 7820            }
 7821        "
 7822        .unindent(),
 7823    );
 7824    handle_signature_help_request(&mut cx, mocked_response).await;
 7825    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7826        .await;
 7827    cx.editor(|editor, _| {
 7828        let signature_help_state = editor.signature_help_state.popover().cloned();
 7829        assert!(signature_help_state.is_some());
 7830        let ParsedMarkdown {
 7831            text, highlights, ..
 7832        } = signature_help_state.unwrap().parsed_content;
 7833        assert_eq!(text, "param1: u8, param2: u8");
 7834        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7835    });
 7836}
 7837
 7838#[gpui::test]
 7839async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7840    init_test(cx, |_| {});
 7841    cx.update(|cx| {
 7842        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7843            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7844                settings.auto_signature_help = Some(true);
 7845            });
 7846        });
 7847    });
 7848
 7849    let mut cx = EditorLspTestContext::new_rust(
 7850        lsp::ServerCapabilities {
 7851            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7852                ..Default::default()
 7853            }),
 7854            ..Default::default()
 7855        },
 7856        cx,
 7857    )
 7858    .await;
 7859
 7860    // A test that directly calls `show_signature_help`
 7861    cx.update_editor(|editor, cx| {
 7862        editor.show_signature_help(&ShowSignatureHelp, cx);
 7863    });
 7864
 7865    let mocked_response = lsp::SignatureHelp {
 7866        signatures: vec![lsp::SignatureInformation {
 7867            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7868            documentation: None,
 7869            parameters: Some(vec![
 7870                lsp::ParameterInformation {
 7871                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7872                    documentation: None,
 7873                },
 7874                lsp::ParameterInformation {
 7875                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7876                    documentation: None,
 7877                },
 7878            ]),
 7879            active_parameter: None,
 7880        }],
 7881        active_signature: Some(0),
 7882        active_parameter: Some(0),
 7883    };
 7884    handle_signature_help_request(&mut cx, mocked_response).await;
 7885
 7886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7887        .await;
 7888
 7889    cx.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    });
 7898
 7899    // When exiting outside from inside the brackets, `signature_help` is closed.
 7900    cx.set_state(indoc! {"
 7901        fn main() {
 7902            sample(ˇ);
 7903        }
 7904
 7905        fn sample(param1: u8, param2: u8) {}
 7906    "});
 7907
 7908    cx.update_editor(|editor, cx| {
 7909        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7910    });
 7911
 7912    let mocked_response = lsp::SignatureHelp {
 7913        signatures: Vec::new(),
 7914        active_signature: None,
 7915        active_parameter: None,
 7916    };
 7917    handle_signature_help_request(&mut cx, mocked_response).await;
 7918
 7919    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7920        .await;
 7921
 7922    cx.editor(|editor, _| {
 7923        assert!(!editor.signature_help_state.is_shown());
 7924    });
 7925
 7926    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7927    cx.set_state(indoc! {"
 7928        fn main() {
 7929            sample(ˇ);
 7930        }
 7931
 7932        fn sample(param1: u8, param2: u8) {}
 7933    "});
 7934
 7935    let mocked_response = lsp::SignatureHelp {
 7936        signatures: vec![lsp::SignatureInformation {
 7937            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7938            documentation: None,
 7939            parameters: Some(vec![
 7940                lsp::ParameterInformation {
 7941                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7942                    documentation: None,
 7943                },
 7944                lsp::ParameterInformation {
 7945                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7946                    documentation: None,
 7947                },
 7948            ]),
 7949            active_parameter: None,
 7950        }],
 7951        active_signature: Some(0),
 7952        active_parameter: Some(0),
 7953    };
 7954    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7955    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7956        .await;
 7957    cx.editor(|editor, _| {
 7958        assert!(editor.signature_help_state.is_shown());
 7959    });
 7960
 7961    // Restore the popover with more parameter input
 7962    cx.set_state(indoc! {"
 7963        fn main() {
 7964            sample(param1, param2ˇ);
 7965        }
 7966
 7967        fn sample(param1: u8, param2: u8) {}
 7968    "});
 7969
 7970    let mocked_response = lsp::SignatureHelp {
 7971        signatures: vec![lsp::SignatureInformation {
 7972            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7973            documentation: None,
 7974            parameters: Some(vec![
 7975                lsp::ParameterInformation {
 7976                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7977                    documentation: None,
 7978                },
 7979                lsp::ParameterInformation {
 7980                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7981                    documentation: None,
 7982                },
 7983            ]),
 7984            active_parameter: None,
 7985        }],
 7986        active_signature: Some(0),
 7987        active_parameter: Some(1),
 7988    };
 7989    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7990    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7991        .await;
 7992
 7993    // When selecting a range, the popover is gone.
 7994    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7995    cx.update_editor(|editor, cx| {
 7996        editor.change_selections(None, cx, |s| {
 7997            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7998        })
 7999    });
 8000    cx.assert_editor_state(indoc! {"
 8001        fn main() {
 8002            sample(param1, «ˇparam2»);
 8003        }
 8004
 8005        fn sample(param1: u8, param2: u8) {}
 8006    "});
 8007    cx.editor(|editor, _| {
 8008        assert!(!editor.signature_help_state.is_shown());
 8009    });
 8010
 8011    // When unselecting again, the popover is back if within the brackets.
 8012    cx.update_editor(|editor, cx| {
 8013        editor.change_selections(None, cx, |s| {
 8014            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8015        })
 8016    });
 8017    cx.assert_editor_state(indoc! {"
 8018        fn main() {
 8019            sample(param1, ˇparam2);
 8020        }
 8021
 8022        fn sample(param1: u8, param2: u8) {}
 8023    "});
 8024    handle_signature_help_request(&mut cx, mocked_response).await;
 8025    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8026        .await;
 8027    cx.editor(|editor, _| {
 8028        assert!(editor.signature_help_state.is_shown());
 8029    });
 8030
 8031    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8032    cx.update_editor(|editor, cx| {
 8033        editor.change_selections(None, cx, |s| {
 8034            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8035            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8036        })
 8037    });
 8038    cx.assert_editor_state(indoc! {"
 8039        fn main() {
 8040            sample(param1, ˇparam2);
 8041        }
 8042
 8043        fn sample(param1: u8, param2: u8) {}
 8044    "});
 8045
 8046    let mocked_response = lsp::SignatureHelp {
 8047        signatures: vec![lsp::SignatureInformation {
 8048            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8049            documentation: None,
 8050            parameters: Some(vec![
 8051                lsp::ParameterInformation {
 8052                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8053                    documentation: None,
 8054                },
 8055                lsp::ParameterInformation {
 8056                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8057                    documentation: None,
 8058                },
 8059            ]),
 8060            active_parameter: None,
 8061        }],
 8062        active_signature: Some(0),
 8063        active_parameter: Some(1),
 8064    };
 8065    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8066    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8067        .await;
 8068    cx.update_editor(|editor, cx| {
 8069        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8070    });
 8071    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8072        .await;
 8073    cx.update_editor(|editor, cx| {
 8074        editor.change_selections(None, cx, |s| {
 8075            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8076        })
 8077    });
 8078    cx.assert_editor_state(indoc! {"
 8079        fn main() {
 8080            sample(param1, «ˇparam2»);
 8081        }
 8082
 8083        fn sample(param1: u8, param2: u8) {}
 8084    "});
 8085    cx.update_editor(|editor, cx| {
 8086        editor.change_selections(None, cx, |s| {
 8087            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8088        })
 8089    });
 8090    cx.assert_editor_state(indoc! {"
 8091        fn main() {
 8092            sample(param1, ˇparam2);
 8093        }
 8094
 8095        fn sample(param1: u8, param2: u8) {}
 8096    "});
 8097    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8098        .await;
 8099}
 8100
 8101#[gpui::test]
 8102async fn test_completion(cx: &mut gpui::TestAppContext) {
 8103    init_test(cx, |_| {});
 8104
 8105    let mut cx = EditorLspTestContext::new_rust(
 8106        lsp::ServerCapabilities {
 8107            completion_provider: Some(lsp::CompletionOptions {
 8108                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8109                resolve_provider: Some(true),
 8110                ..Default::default()
 8111            }),
 8112            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8113            ..Default::default()
 8114        },
 8115        cx,
 8116    )
 8117    .await;
 8118    let counter = Arc::new(AtomicUsize::new(0));
 8119
 8120    cx.set_state(indoc! {"
 8121        oneˇ
 8122        two
 8123        three
 8124    "});
 8125    cx.simulate_keystroke(".");
 8126    handle_completion_request(
 8127        &mut cx,
 8128        indoc! {"
 8129            one.|<>
 8130            two
 8131            three
 8132        "},
 8133        vec!["first_completion", "second_completion"],
 8134        counter.clone(),
 8135    )
 8136    .await;
 8137    cx.condition(|editor, _| editor.context_menu_visible())
 8138        .await;
 8139    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8140
 8141    let _handler = handle_signature_help_request(
 8142        &mut cx,
 8143        lsp::SignatureHelp {
 8144            signatures: vec![lsp::SignatureInformation {
 8145                label: "test signature".to_string(),
 8146                documentation: None,
 8147                parameters: Some(vec![lsp::ParameterInformation {
 8148                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8149                    documentation: None,
 8150                }]),
 8151                active_parameter: None,
 8152            }],
 8153            active_signature: None,
 8154            active_parameter: None,
 8155        },
 8156    );
 8157    cx.update_editor(|editor, cx| {
 8158        assert!(
 8159            !editor.signature_help_state.is_shown(),
 8160            "No signature help was called for"
 8161        );
 8162        editor.show_signature_help(&ShowSignatureHelp, cx);
 8163    });
 8164    cx.run_until_parked();
 8165    cx.update_editor(|editor, _| {
 8166        assert!(
 8167            !editor.signature_help_state.is_shown(),
 8168            "No signature help should be shown when completions menu is open"
 8169        );
 8170    });
 8171
 8172    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8173        editor.context_menu_next(&Default::default(), cx);
 8174        editor
 8175            .confirm_completion(&ConfirmCompletion::default(), cx)
 8176            .unwrap()
 8177    });
 8178    cx.assert_editor_state(indoc! {"
 8179        one.second_completionˇ
 8180        two
 8181        three
 8182    "});
 8183
 8184    handle_resolve_completion_request(
 8185        &mut cx,
 8186        Some(vec![
 8187            (
 8188                //This overlaps with the primary completion edit which is
 8189                //misbehavior from the LSP spec, test that we filter it out
 8190                indoc! {"
 8191                    one.second_ˇcompletion
 8192                    two
 8193                    threeˇ
 8194                "},
 8195                "overlapping additional edit",
 8196            ),
 8197            (
 8198                indoc! {"
 8199                    one.second_completion
 8200                    two
 8201                    threeˇ
 8202                "},
 8203                "\nadditional edit",
 8204            ),
 8205        ]),
 8206    )
 8207    .await;
 8208    apply_additional_edits.await.unwrap();
 8209    cx.assert_editor_state(indoc! {"
 8210        one.second_completionˇ
 8211        two
 8212        three
 8213        additional edit
 8214    "});
 8215
 8216    cx.set_state(indoc! {"
 8217        one.second_completion
 8218        twoˇ
 8219        threeˇ
 8220        additional edit
 8221    "});
 8222    cx.simulate_keystroke(" ");
 8223    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8224    cx.simulate_keystroke("s");
 8225    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8226
 8227    cx.assert_editor_state(indoc! {"
 8228        one.second_completion
 8229        two sˇ
 8230        three sˇ
 8231        additional edit
 8232    "});
 8233    handle_completion_request(
 8234        &mut cx,
 8235        indoc! {"
 8236            one.second_completion
 8237            two s
 8238            three <s|>
 8239            additional edit
 8240        "},
 8241        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8242        counter.clone(),
 8243    )
 8244    .await;
 8245    cx.condition(|editor, _| editor.context_menu_visible())
 8246        .await;
 8247    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8248
 8249    cx.simulate_keystroke("i");
 8250
 8251    handle_completion_request(
 8252        &mut cx,
 8253        indoc! {"
 8254            one.second_completion
 8255            two si
 8256            three <si|>
 8257            additional edit
 8258        "},
 8259        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8260        counter.clone(),
 8261    )
 8262    .await;
 8263    cx.condition(|editor, _| editor.context_menu_visible())
 8264        .await;
 8265    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8266
 8267    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8268        editor
 8269            .confirm_completion(&ConfirmCompletion::default(), cx)
 8270            .unwrap()
 8271    });
 8272    cx.assert_editor_state(indoc! {"
 8273        one.second_completion
 8274        two sixth_completionˇ
 8275        three sixth_completionˇ
 8276        additional edit
 8277    "});
 8278
 8279    handle_resolve_completion_request(&mut cx, None).await;
 8280    apply_additional_edits.await.unwrap();
 8281
 8282    cx.update(|cx| {
 8283        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8284            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8285                settings.show_completions_on_input = Some(false);
 8286            });
 8287        })
 8288    });
 8289    cx.set_state("editorˇ");
 8290    cx.simulate_keystroke(".");
 8291    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8292    cx.simulate_keystroke("c");
 8293    cx.simulate_keystroke("l");
 8294    cx.simulate_keystroke("o");
 8295    cx.assert_editor_state("editor.cloˇ");
 8296    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8297    cx.update_editor(|editor, cx| {
 8298        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8299    });
 8300    handle_completion_request(
 8301        &mut cx,
 8302        "editor.<clo|>",
 8303        vec!["close", "clobber"],
 8304        counter.clone(),
 8305    )
 8306    .await;
 8307    cx.condition(|editor, _| editor.context_menu_visible())
 8308        .await;
 8309    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8310
 8311    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8312        editor
 8313            .confirm_completion(&ConfirmCompletion::default(), cx)
 8314            .unwrap()
 8315    });
 8316    cx.assert_editor_state("editor.closeˇ");
 8317    handle_resolve_completion_request(&mut cx, None).await;
 8318    apply_additional_edits.await.unwrap();
 8319}
 8320
 8321#[gpui::test]
 8322async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8323    init_test(cx, |_| {});
 8324    let mut cx = EditorLspTestContext::new_rust(
 8325        lsp::ServerCapabilities {
 8326            completion_provider: Some(lsp::CompletionOptions {
 8327                trigger_characters: Some(vec![".".to_string()]),
 8328                ..Default::default()
 8329            }),
 8330            ..Default::default()
 8331        },
 8332        cx,
 8333    )
 8334    .await;
 8335    cx.lsp
 8336        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8337            Ok(Some(lsp::CompletionResponse::Array(vec![
 8338                lsp::CompletionItem {
 8339                    label: "first".into(),
 8340                    ..Default::default()
 8341                },
 8342                lsp::CompletionItem {
 8343                    label: "last".into(),
 8344                    ..Default::default()
 8345                },
 8346            ])))
 8347        });
 8348    cx.set_state("variableˇ");
 8349    cx.simulate_keystroke(".");
 8350    cx.executor().run_until_parked();
 8351
 8352    cx.update_editor(|editor, _| {
 8353        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8354            assert_eq!(
 8355                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8356                &["first", "last"]
 8357            );
 8358        } else {
 8359            panic!("expected completion menu to be open");
 8360        }
 8361    });
 8362
 8363    cx.update_editor(|editor, cx| {
 8364        editor.move_page_down(&MovePageDown::default(), cx);
 8365        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8366            assert!(
 8367                menu.selected_item == 1,
 8368                "expected PageDown to select the last item from the context menu"
 8369            );
 8370        } else {
 8371            panic!("expected completion menu to stay open after PageDown");
 8372        }
 8373    });
 8374
 8375    cx.update_editor(|editor, cx| {
 8376        editor.move_page_up(&MovePageUp::default(), cx);
 8377        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8378            assert!(
 8379                menu.selected_item == 0,
 8380                "expected PageUp to select the first item from the context menu"
 8381            );
 8382        } else {
 8383            panic!("expected completion menu to stay open after PageUp");
 8384        }
 8385    });
 8386}
 8387
 8388#[gpui::test]
 8389async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8390    init_test(cx, |_| {});
 8391
 8392    let mut cx = EditorLspTestContext::new_rust(
 8393        lsp::ServerCapabilities {
 8394            completion_provider: Some(lsp::CompletionOptions {
 8395                trigger_characters: Some(vec![".".to_string()]),
 8396                resolve_provider: Some(true),
 8397                ..Default::default()
 8398            }),
 8399            ..Default::default()
 8400        },
 8401        cx,
 8402    )
 8403    .await;
 8404
 8405    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8406    cx.simulate_keystroke(".");
 8407    let completion_item = lsp::CompletionItem {
 8408        label: "Some".into(),
 8409        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8410        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8411        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8412            kind: lsp::MarkupKind::Markdown,
 8413            value: "```rust\nSome(2)\n```".to_string(),
 8414        })),
 8415        deprecated: Some(false),
 8416        sort_text: Some("Some".to_string()),
 8417        filter_text: Some("Some".to_string()),
 8418        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8419        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8420            range: lsp::Range {
 8421                start: lsp::Position {
 8422                    line: 0,
 8423                    character: 22,
 8424                },
 8425                end: lsp::Position {
 8426                    line: 0,
 8427                    character: 22,
 8428                },
 8429            },
 8430            new_text: "Some(2)".to_string(),
 8431        })),
 8432        additional_text_edits: Some(vec![lsp::TextEdit {
 8433            range: lsp::Range {
 8434                start: lsp::Position {
 8435                    line: 0,
 8436                    character: 20,
 8437                },
 8438                end: lsp::Position {
 8439                    line: 0,
 8440                    character: 22,
 8441                },
 8442            },
 8443            new_text: "".to_string(),
 8444        }]),
 8445        ..Default::default()
 8446    };
 8447
 8448    let closure_completion_item = completion_item.clone();
 8449    let counter = Arc::new(AtomicUsize::new(0));
 8450    let counter_clone = counter.clone();
 8451    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8452        let task_completion_item = closure_completion_item.clone();
 8453        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8454        async move {
 8455            Ok(Some(lsp::CompletionResponse::Array(vec![
 8456                task_completion_item,
 8457            ])))
 8458        }
 8459    });
 8460
 8461    cx.condition(|editor, _| editor.context_menu_visible())
 8462        .await;
 8463    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8464    assert!(request.next().await.is_some());
 8465    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8466
 8467    cx.simulate_keystroke("S");
 8468    cx.simulate_keystroke("o");
 8469    cx.simulate_keystroke("m");
 8470    cx.condition(|editor, _| editor.context_menu_visible())
 8471        .await;
 8472    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8473    assert!(request.next().await.is_some());
 8474    assert!(request.next().await.is_some());
 8475    assert!(request.next().await.is_some());
 8476    request.close();
 8477    assert!(request.next().await.is_none());
 8478    assert_eq!(
 8479        counter.load(atomic::Ordering::Acquire),
 8480        4,
 8481        "With the completions menu open, only one LSP request should happen per input"
 8482    );
 8483}
 8484
 8485#[gpui::test]
 8486async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8487    init_test(cx, |_| {});
 8488    let mut cx = EditorTestContext::new(cx).await;
 8489    let language = Arc::new(Language::new(
 8490        LanguageConfig {
 8491            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8492            ..Default::default()
 8493        },
 8494        Some(tree_sitter_rust::LANGUAGE.into()),
 8495    ));
 8496    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8497
 8498    // If multiple selections intersect a line, the line is only toggled once.
 8499    cx.set_state(indoc! {"
 8500        fn a() {
 8501            «//b();
 8502            ˇ»// «c();
 8503            //ˇ»  d();
 8504        }
 8505    "});
 8506
 8507    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8508
 8509    cx.assert_editor_state(indoc! {"
 8510        fn a() {
 8511            «b();
 8512            c();
 8513            ˇ» d();
 8514        }
 8515    "});
 8516
 8517    // The comment prefix is inserted at the same column for every line in a
 8518    // selection.
 8519    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8520
 8521    cx.assert_editor_state(indoc! {"
 8522        fn a() {
 8523            // «b();
 8524            // c();
 8525            ˇ»//  d();
 8526        }
 8527    "});
 8528
 8529    // If a selection ends at the beginning of a line, that line is not toggled.
 8530    cx.set_selections_state(indoc! {"
 8531        fn a() {
 8532            // b();
 8533            «// c();
 8534        ˇ»    //  d();
 8535        }
 8536    "});
 8537
 8538    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8539
 8540    cx.assert_editor_state(indoc! {"
 8541        fn a() {
 8542            // b();
 8543            «c();
 8544        ˇ»    //  d();
 8545        }
 8546    "});
 8547
 8548    // If a selection span a single line and is empty, the line is toggled.
 8549    cx.set_state(indoc! {"
 8550        fn a() {
 8551            a();
 8552            b();
 8553        ˇ
 8554        }
 8555    "});
 8556
 8557    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8558
 8559    cx.assert_editor_state(indoc! {"
 8560        fn a() {
 8561            a();
 8562            b();
 8563        //•ˇ
 8564        }
 8565    "});
 8566
 8567    // If a selection span multiple lines, empty lines are not toggled.
 8568    cx.set_state(indoc! {"
 8569        fn a() {
 8570            «a();
 8571
 8572            c();ˇ»
 8573        }
 8574    "});
 8575
 8576    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8577
 8578    cx.assert_editor_state(indoc! {"
 8579        fn a() {
 8580            // «a();
 8581
 8582            // c();ˇ»
 8583        }
 8584    "});
 8585
 8586    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8587    cx.set_state(indoc! {"
 8588        fn a() {
 8589            «// a();
 8590            /// b();
 8591            //! c();ˇ»
 8592        }
 8593    "});
 8594
 8595    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8596
 8597    cx.assert_editor_state(indoc! {"
 8598        fn a() {
 8599            «a();
 8600            b();
 8601            c();ˇ»
 8602        }
 8603    "});
 8604}
 8605
 8606#[gpui::test]
 8607async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8608    init_test(cx, |_| {});
 8609    let mut cx = EditorTestContext::new(cx).await;
 8610    let language = Arc::new(Language::new(
 8611        LanguageConfig {
 8612            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8613            ..Default::default()
 8614        },
 8615        Some(tree_sitter_rust::LANGUAGE.into()),
 8616    ));
 8617    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8618
 8619    let toggle_comments = &ToggleComments {
 8620        advance_downwards: false,
 8621        ignore_indent: true,
 8622    };
 8623
 8624    // If multiple selections intersect a line, the line is only toggled once.
 8625    cx.set_state(indoc! {"
 8626        fn a() {
 8627        //    «b();
 8628        //    c();
 8629        //    ˇ» d();
 8630        }
 8631    "});
 8632
 8633    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8634
 8635    cx.assert_editor_state(indoc! {"
 8636        fn a() {
 8637            «b();
 8638            c();
 8639            ˇ» d();
 8640        }
 8641    "});
 8642
 8643    // The comment prefix is inserted at the beginning of each line
 8644    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8645
 8646    cx.assert_editor_state(indoc! {"
 8647        fn a() {
 8648        //    «b();
 8649        //    c();
 8650        //    ˇ» d();
 8651        }
 8652    "});
 8653
 8654    // If a selection ends at the beginning of a line, that line is not toggled.
 8655    cx.set_selections_state(indoc! {"
 8656        fn a() {
 8657        //    b();
 8658        //    «c();
 8659        ˇ»//     d();
 8660        }
 8661    "});
 8662
 8663    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8664
 8665    cx.assert_editor_state(indoc! {"
 8666        fn a() {
 8667        //    b();
 8668            «c();
 8669        ˇ»//     d();
 8670        }
 8671    "});
 8672
 8673    // If a selection span a single line and is empty, the line is toggled.
 8674    cx.set_state(indoc! {"
 8675        fn a() {
 8676            a();
 8677            b();
 8678        ˇ
 8679        }
 8680    "});
 8681
 8682    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8683
 8684    cx.assert_editor_state(indoc! {"
 8685        fn a() {
 8686            a();
 8687            b();
 8688        //ˇ
 8689        }
 8690    "});
 8691
 8692    // If a selection span multiple lines, empty lines are not toggled.
 8693    cx.set_state(indoc! {"
 8694        fn a() {
 8695            «a();
 8696
 8697            c();ˇ»
 8698        }
 8699    "});
 8700
 8701    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8702
 8703    cx.assert_editor_state(indoc! {"
 8704        fn a() {
 8705        //    «a();
 8706
 8707        //    c();ˇ»
 8708        }
 8709    "});
 8710
 8711    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8712    cx.set_state(indoc! {"
 8713        fn a() {
 8714        //    «a();
 8715        ///    b();
 8716        //!    c();ˇ»
 8717        }
 8718    "});
 8719
 8720    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8721
 8722    cx.assert_editor_state(indoc! {"
 8723        fn a() {
 8724            «a();
 8725            b();
 8726            c();ˇ»
 8727        }
 8728    "});
 8729}
 8730
 8731#[gpui::test]
 8732async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8733    init_test(cx, |_| {});
 8734
 8735    let language = Arc::new(Language::new(
 8736        LanguageConfig {
 8737            line_comments: vec!["// ".into()],
 8738            ..Default::default()
 8739        },
 8740        Some(tree_sitter_rust::LANGUAGE.into()),
 8741    ));
 8742
 8743    let mut cx = EditorTestContext::new(cx).await;
 8744
 8745    cx.language_registry().add(language.clone());
 8746    cx.update_buffer(|buffer, cx| {
 8747        buffer.set_language(Some(language), cx);
 8748    });
 8749
 8750    let toggle_comments = &ToggleComments {
 8751        advance_downwards: true,
 8752        ignore_indent: false,
 8753    };
 8754
 8755    // Single cursor on one line -> advance
 8756    // Cursor moves horizontally 3 characters as well on non-blank line
 8757    cx.set_state(indoc!(
 8758        "fn a() {
 8759             ˇdog();
 8760             cat();
 8761        }"
 8762    ));
 8763    cx.update_editor(|editor, cx| {
 8764        editor.toggle_comments(toggle_comments, cx);
 8765    });
 8766    cx.assert_editor_state(indoc!(
 8767        "fn a() {
 8768             // dog();
 8769             catˇ();
 8770        }"
 8771    ));
 8772
 8773    // Single selection on one line -> don't advance
 8774    cx.set_state(indoc!(
 8775        "fn a() {
 8776             «dog()ˇ»;
 8777             cat();
 8778        }"
 8779    ));
 8780    cx.update_editor(|editor, cx| {
 8781        editor.toggle_comments(toggle_comments, cx);
 8782    });
 8783    cx.assert_editor_state(indoc!(
 8784        "fn a() {
 8785             // «dog()ˇ»;
 8786             cat();
 8787        }"
 8788    ));
 8789
 8790    // Multiple cursors on one line -> advance
 8791    cx.set_state(indoc!(
 8792        "fn a() {
 8793             ˇdˇog();
 8794             cat();
 8795        }"
 8796    ));
 8797    cx.update_editor(|editor, cx| {
 8798        editor.toggle_comments(toggle_comments, cx);
 8799    });
 8800    cx.assert_editor_state(indoc!(
 8801        "fn a() {
 8802             // dog();
 8803             catˇ(ˇ);
 8804        }"
 8805    ));
 8806
 8807    // Multiple cursors on one line, with selection -> don't advance
 8808    cx.set_state(indoc!(
 8809        "fn a() {
 8810             ˇdˇog«()ˇ»;
 8811             cat();
 8812        }"
 8813    ));
 8814    cx.update_editor(|editor, cx| {
 8815        editor.toggle_comments(toggle_comments, cx);
 8816    });
 8817    cx.assert_editor_state(indoc!(
 8818        "fn a() {
 8819             // ˇdˇog«()ˇ»;
 8820             cat();
 8821        }"
 8822    ));
 8823
 8824    // Single cursor on one line -> advance
 8825    // Cursor moves to column 0 on blank line
 8826    cx.set_state(indoc!(
 8827        "fn a() {
 8828             ˇdog();
 8829
 8830             cat();
 8831        }"
 8832    ));
 8833    cx.update_editor(|editor, cx| {
 8834        editor.toggle_comments(toggle_comments, cx);
 8835    });
 8836    cx.assert_editor_state(indoc!(
 8837        "fn a() {
 8838             // dog();
 8839        ˇ
 8840             cat();
 8841        }"
 8842    ));
 8843
 8844    // Single cursor on one line -> advance
 8845    // Cursor starts and ends at column 0
 8846    cx.set_state(indoc!(
 8847        "fn a() {
 8848         ˇ    dog();
 8849             cat();
 8850        }"
 8851    ));
 8852    cx.update_editor(|editor, cx| {
 8853        editor.toggle_comments(toggle_comments, cx);
 8854    });
 8855    cx.assert_editor_state(indoc!(
 8856        "fn a() {
 8857             // dog();
 8858         ˇ    cat();
 8859        }"
 8860    ));
 8861}
 8862
 8863#[gpui::test]
 8864async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8865    init_test(cx, |_| {});
 8866
 8867    let mut cx = EditorTestContext::new(cx).await;
 8868
 8869    let html_language = Arc::new(
 8870        Language::new(
 8871            LanguageConfig {
 8872                name: "HTML".into(),
 8873                block_comment: Some(("<!-- ".into(), " -->".into())),
 8874                ..Default::default()
 8875            },
 8876            Some(tree_sitter_html::language()),
 8877        )
 8878        .with_injection_query(
 8879            r#"
 8880            (script_element
 8881                (raw_text) @content
 8882                (#set! "language" "javascript"))
 8883            "#,
 8884        )
 8885        .unwrap(),
 8886    );
 8887
 8888    let javascript_language = Arc::new(Language::new(
 8889        LanguageConfig {
 8890            name: "JavaScript".into(),
 8891            line_comments: vec!["// ".into()],
 8892            ..Default::default()
 8893        },
 8894        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8895    ));
 8896
 8897    cx.language_registry().add(html_language.clone());
 8898    cx.language_registry().add(javascript_language.clone());
 8899    cx.update_buffer(|buffer, cx| {
 8900        buffer.set_language(Some(html_language), cx);
 8901    });
 8902
 8903    // Toggle comments for empty selections
 8904    cx.set_state(
 8905        &r#"
 8906            <p>A</p>ˇ
 8907            <p>B</p>ˇ
 8908            <p>C</p>ˇ
 8909        "#
 8910        .unindent(),
 8911    );
 8912    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8913    cx.assert_editor_state(
 8914        &r#"
 8915            <!-- <p>A</p>ˇ -->
 8916            <!-- <p>B</p>ˇ -->
 8917            <!-- <p>C</p>ˇ -->
 8918        "#
 8919        .unindent(),
 8920    );
 8921    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8922    cx.assert_editor_state(
 8923        &r#"
 8924            <p>A</p>ˇ
 8925            <p>B</p>ˇ
 8926            <p>C</p>ˇ
 8927        "#
 8928        .unindent(),
 8929    );
 8930
 8931    // Toggle comments for mixture of empty and non-empty selections, where
 8932    // multiple selections occupy a given line.
 8933    cx.set_state(
 8934        &r#"
 8935            <p>A«</p>
 8936            <p>ˇ»B</p>ˇ
 8937            <p>C«</p>
 8938            <p>ˇ»D</p>ˇ
 8939        "#
 8940        .unindent(),
 8941    );
 8942
 8943    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8944    cx.assert_editor_state(
 8945        &r#"
 8946            <!-- <p>A«</p>
 8947            <p>ˇ»B</p>ˇ -->
 8948            <!-- <p>C«</p>
 8949            <p>ˇ»D</p>ˇ -->
 8950        "#
 8951        .unindent(),
 8952    );
 8953    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8954    cx.assert_editor_state(
 8955        &r#"
 8956            <p>A«</p>
 8957            <p>ˇ»B</p>ˇ
 8958            <p>C«</p>
 8959            <p>ˇ»D</p>ˇ
 8960        "#
 8961        .unindent(),
 8962    );
 8963
 8964    // Toggle comments when different languages are active for different
 8965    // selections.
 8966    cx.set_state(
 8967        &r#"
 8968            ˇ<script>
 8969                ˇvar x = new Y();
 8970            ˇ</script>
 8971        "#
 8972        .unindent(),
 8973    );
 8974    cx.executor().run_until_parked();
 8975    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8976    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8977    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8978    cx.assert_editor_state(
 8979        &r#"
 8980            <!-- ˇ<script> -->
 8981                // ˇvar x = new Y();
 8982            // ˇ</script>
 8983        "#
 8984        .unindent(),
 8985    );
 8986}
 8987
 8988#[gpui::test]
 8989fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8990    init_test(cx, |_| {});
 8991
 8992    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8993    let multibuffer = cx.new_model(|cx| {
 8994        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8995        multibuffer.push_excerpts(
 8996            buffer.clone(),
 8997            [
 8998                ExcerptRange {
 8999                    context: Point::new(0, 0)..Point::new(0, 4),
 9000                    primary: None,
 9001                },
 9002                ExcerptRange {
 9003                    context: Point::new(1, 0)..Point::new(1, 4),
 9004                    primary: None,
 9005                },
 9006            ],
 9007            cx,
 9008        );
 9009        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9010        multibuffer
 9011    });
 9012
 9013    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9014    view.update(cx, |view, cx| {
 9015        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9016        view.change_selections(None, cx, |s| {
 9017            s.select_ranges([
 9018                Point::new(0, 0)..Point::new(0, 0),
 9019                Point::new(1, 0)..Point::new(1, 0),
 9020            ])
 9021        });
 9022
 9023        view.handle_input("X", cx);
 9024        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9025        assert_eq!(
 9026            view.selections.ranges(cx),
 9027            [
 9028                Point::new(0, 1)..Point::new(0, 1),
 9029                Point::new(1, 1)..Point::new(1, 1),
 9030            ]
 9031        );
 9032
 9033        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9034        view.change_selections(None, cx, |s| {
 9035            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9036        });
 9037        view.backspace(&Default::default(), cx);
 9038        assert_eq!(view.text(cx), "Xa\nbbb");
 9039        assert_eq!(
 9040            view.selections.ranges(cx),
 9041            [Point::new(1, 0)..Point::new(1, 0)]
 9042        );
 9043
 9044        view.change_selections(None, cx, |s| {
 9045            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9046        });
 9047        view.backspace(&Default::default(), cx);
 9048        assert_eq!(view.text(cx), "X\nbb");
 9049        assert_eq!(
 9050            view.selections.ranges(cx),
 9051            [Point::new(0, 1)..Point::new(0, 1)]
 9052        );
 9053    });
 9054}
 9055
 9056#[gpui::test]
 9057fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9058    init_test(cx, |_| {});
 9059
 9060    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9061    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9062        indoc! {"
 9063            [aaaa
 9064            (bbbb]
 9065            cccc)",
 9066        },
 9067        markers.clone(),
 9068    );
 9069    let excerpt_ranges = markers.into_iter().map(|marker| {
 9070        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9071        ExcerptRange {
 9072            context,
 9073            primary: None,
 9074        }
 9075    });
 9076    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9077    let multibuffer = cx.new_model(|cx| {
 9078        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9079        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9080        multibuffer
 9081    });
 9082
 9083    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9084    view.update(cx, |view, cx| {
 9085        let (expected_text, selection_ranges) = marked_text_ranges(
 9086            indoc! {"
 9087                aaaa
 9088                bˇbbb
 9089                bˇbbˇb
 9090                cccc"
 9091            },
 9092            true,
 9093        );
 9094        assert_eq!(view.text(cx), expected_text);
 9095        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9096
 9097        view.handle_input("X", cx);
 9098
 9099        let (expected_text, expected_selections) = marked_text_ranges(
 9100            indoc! {"
 9101                aaaa
 9102                bXˇbbXb
 9103                bXˇbbXˇb
 9104                cccc"
 9105            },
 9106            false,
 9107        );
 9108        assert_eq!(view.text(cx), expected_text);
 9109        assert_eq!(view.selections.ranges(cx), expected_selections);
 9110
 9111        view.newline(&Newline, cx);
 9112        let (expected_text, expected_selections) = marked_text_ranges(
 9113            indoc! {"
 9114                aaaa
 9115                bX
 9116                ˇbbX
 9117                b
 9118                bX
 9119                ˇbbX
 9120                ˇb
 9121                cccc"
 9122            },
 9123            false,
 9124        );
 9125        assert_eq!(view.text(cx), expected_text);
 9126        assert_eq!(view.selections.ranges(cx), expected_selections);
 9127    });
 9128}
 9129
 9130#[gpui::test]
 9131fn test_refresh_selections(cx: &mut TestAppContext) {
 9132    init_test(cx, |_| {});
 9133
 9134    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9135    let mut excerpt1_id = None;
 9136    let multibuffer = cx.new_model(|cx| {
 9137        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9138        excerpt1_id = multibuffer
 9139            .push_excerpts(
 9140                buffer.clone(),
 9141                [
 9142                    ExcerptRange {
 9143                        context: Point::new(0, 0)..Point::new(1, 4),
 9144                        primary: None,
 9145                    },
 9146                    ExcerptRange {
 9147                        context: Point::new(1, 0)..Point::new(2, 4),
 9148                        primary: None,
 9149                    },
 9150                ],
 9151                cx,
 9152            )
 9153            .into_iter()
 9154            .next();
 9155        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9156        multibuffer
 9157    });
 9158
 9159    let editor = cx.add_window(|cx| {
 9160        let mut editor = build_editor(multibuffer.clone(), cx);
 9161        let snapshot = editor.snapshot(cx);
 9162        editor.change_selections(None, cx, |s| {
 9163            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9164        });
 9165        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9166        assert_eq!(
 9167            editor.selections.ranges(cx),
 9168            [
 9169                Point::new(1, 3)..Point::new(1, 3),
 9170                Point::new(2, 1)..Point::new(2, 1),
 9171            ]
 9172        );
 9173        editor
 9174    });
 9175
 9176    // Refreshing selections is a no-op when excerpts haven't changed.
 9177    _ = editor.update(cx, |editor, cx| {
 9178        editor.change_selections(None, cx, |s| s.refresh());
 9179        assert_eq!(
 9180            editor.selections.ranges(cx),
 9181            [
 9182                Point::new(1, 3)..Point::new(1, 3),
 9183                Point::new(2, 1)..Point::new(2, 1),
 9184            ]
 9185        );
 9186    });
 9187
 9188    multibuffer.update(cx, |multibuffer, cx| {
 9189        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9190    });
 9191    _ = editor.update(cx, |editor, cx| {
 9192        // Removing an excerpt causes the first selection to become degenerate.
 9193        assert_eq!(
 9194            editor.selections.ranges(cx),
 9195            [
 9196                Point::new(0, 0)..Point::new(0, 0),
 9197                Point::new(0, 1)..Point::new(0, 1)
 9198            ]
 9199        );
 9200
 9201        // Refreshing selections will relocate the first selection to the original buffer
 9202        // location.
 9203        editor.change_selections(None, cx, |s| s.refresh());
 9204        assert_eq!(
 9205            editor.selections.ranges(cx),
 9206            [
 9207                Point::new(0, 1)..Point::new(0, 1),
 9208                Point::new(0, 3)..Point::new(0, 3)
 9209            ]
 9210        );
 9211        assert!(editor.selections.pending_anchor().is_some());
 9212    });
 9213}
 9214
 9215#[gpui::test]
 9216fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9217    init_test(cx, |_| {});
 9218
 9219    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9220    let mut excerpt1_id = None;
 9221    let multibuffer = cx.new_model(|cx| {
 9222        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9223        excerpt1_id = multibuffer
 9224            .push_excerpts(
 9225                buffer.clone(),
 9226                [
 9227                    ExcerptRange {
 9228                        context: Point::new(0, 0)..Point::new(1, 4),
 9229                        primary: None,
 9230                    },
 9231                    ExcerptRange {
 9232                        context: Point::new(1, 0)..Point::new(2, 4),
 9233                        primary: None,
 9234                    },
 9235                ],
 9236                cx,
 9237            )
 9238            .into_iter()
 9239            .next();
 9240        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9241        multibuffer
 9242    });
 9243
 9244    let editor = cx.add_window(|cx| {
 9245        let mut editor = build_editor(multibuffer.clone(), cx);
 9246        let snapshot = editor.snapshot(cx);
 9247        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9248        assert_eq!(
 9249            editor.selections.ranges(cx),
 9250            [Point::new(1, 3)..Point::new(1, 3)]
 9251        );
 9252        editor
 9253    });
 9254
 9255    multibuffer.update(cx, |multibuffer, cx| {
 9256        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9257    });
 9258    _ = editor.update(cx, |editor, cx| {
 9259        assert_eq!(
 9260            editor.selections.ranges(cx),
 9261            [Point::new(0, 0)..Point::new(0, 0)]
 9262        );
 9263
 9264        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9265        editor.change_selections(None, cx, |s| s.refresh());
 9266        assert_eq!(
 9267            editor.selections.ranges(cx),
 9268            [Point::new(0, 3)..Point::new(0, 3)]
 9269        );
 9270        assert!(editor.selections.pending_anchor().is_some());
 9271    });
 9272}
 9273
 9274#[gpui::test]
 9275async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9276    init_test(cx, |_| {});
 9277
 9278    let language = Arc::new(
 9279        Language::new(
 9280            LanguageConfig {
 9281                brackets: BracketPairConfig {
 9282                    pairs: vec![
 9283                        BracketPair {
 9284                            start: "{".to_string(),
 9285                            end: "}".to_string(),
 9286                            close: true,
 9287                            surround: true,
 9288                            newline: true,
 9289                        },
 9290                        BracketPair {
 9291                            start: "/* ".to_string(),
 9292                            end: " */".to_string(),
 9293                            close: true,
 9294                            surround: true,
 9295                            newline: true,
 9296                        },
 9297                    ],
 9298                    ..Default::default()
 9299                },
 9300                ..Default::default()
 9301            },
 9302            Some(tree_sitter_rust::LANGUAGE.into()),
 9303        )
 9304        .with_indents_query("")
 9305        .unwrap(),
 9306    );
 9307
 9308    let text = concat!(
 9309        "{   }\n",     //
 9310        "  x\n",       //
 9311        "  /*   */\n", //
 9312        "x\n",         //
 9313        "{{} }\n",     //
 9314    );
 9315
 9316    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9317    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9318    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9319    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9320        .await;
 9321
 9322    view.update(cx, |view, cx| {
 9323        view.change_selections(None, cx, |s| {
 9324            s.select_display_ranges([
 9325                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9326                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9327                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9328            ])
 9329        });
 9330        view.newline(&Newline, cx);
 9331
 9332        assert_eq!(
 9333            view.buffer().read(cx).read(cx).text(),
 9334            concat!(
 9335                "{ \n",    // Suppress rustfmt
 9336                "\n",      //
 9337                "}\n",     //
 9338                "  x\n",   //
 9339                "  /* \n", //
 9340                "  \n",    //
 9341                "  */\n",  //
 9342                "x\n",     //
 9343                "{{} \n",  //
 9344                "}\n",     //
 9345            )
 9346        );
 9347    });
 9348}
 9349
 9350#[gpui::test]
 9351fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9352    init_test(cx, |_| {});
 9353
 9354    let editor = cx.add_window(|cx| {
 9355        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9356        build_editor(buffer.clone(), cx)
 9357    });
 9358
 9359    _ = editor.update(cx, |editor, cx| {
 9360        struct Type1;
 9361        struct Type2;
 9362
 9363        let buffer = editor.buffer.read(cx).snapshot(cx);
 9364
 9365        let anchor_range =
 9366            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9367
 9368        editor.highlight_background::<Type1>(
 9369            &[
 9370                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9371                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9372                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9373                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9374            ],
 9375            |_| Hsla::red(),
 9376            cx,
 9377        );
 9378        editor.highlight_background::<Type2>(
 9379            &[
 9380                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9381                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9382                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9383                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9384            ],
 9385            |_| Hsla::green(),
 9386            cx,
 9387        );
 9388
 9389        let snapshot = editor.snapshot(cx);
 9390        let mut highlighted_ranges = editor.background_highlights_in_range(
 9391            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9392            &snapshot,
 9393            cx.theme().colors(),
 9394        );
 9395        // Enforce a consistent ordering based on color without relying on the ordering of the
 9396        // highlight's `TypeId` which is non-executor.
 9397        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9398        assert_eq!(
 9399            highlighted_ranges,
 9400            &[
 9401                (
 9402                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9403                    Hsla::red(),
 9404                ),
 9405                (
 9406                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9407                    Hsla::red(),
 9408                ),
 9409                (
 9410                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9411                    Hsla::green(),
 9412                ),
 9413                (
 9414                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9415                    Hsla::green(),
 9416                ),
 9417            ]
 9418        );
 9419        assert_eq!(
 9420            editor.background_highlights_in_range(
 9421                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9422                &snapshot,
 9423                cx.theme().colors(),
 9424            ),
 9425            &[(
 9426                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9427                Hsla::red(),
 9428            )]
 9429        );
 9430    });
 9431}
 9432
 9433#[gpui::test]
 9434async fn test_following(cx: &mut gpui::TestAppContext) {
 9435    init_test(cx, |_| {});
 9436
 9437    let fs = FakeFs::new(cx.executor());
 9438    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9439
 9440    let buffer = project.update(cx, |project, cx| {
 9441        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9442        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9443    });
 9444    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9445    let follower = cx.update(|cx| {
 9446        cx.open_window(
 9447            WindowOptions {
 9448                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9449                    gpui::Point::new(px(0.), px(0.)),
 9450                    gpui::Point::new(px(10.), px(80.)),
 9451                ))),
 9452                ..Default::default()
 9453            },
 9454            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9455        )
 9456        .unwrap()
 9457    });
 9458
 9459    let is_still_following = Rc::new(RefCell::new(true));
 9460    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9461    let pending_update = Rc::new(RefCell::new(None));
 9462    _ = follower.update(cx, {
 9463        let update = pending_update.clone();
 9464        let is_still_following = is_still_following.clone();
 9465        let follower_edit_event_count = follower_edit_event_count.clone();
 9466        |_, cx| {
 9467            cx.subscribe(
 9468                &leader.root_view(cx).unwrap(),
 9469                move |_, leader, event, cx| {
 9470                    leader
 9471                        .read(cx)
 9472                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9473                },
 9474            )
 9475            .detach();
 9476
 9477            cx.subscribe(
 9478                &follower.root_view(cx).unwrap(),
 9479                move |_, _, event: &EditorEvent, _cx| {
 9480                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9481                        *is_still_following.borrow_mut() = false;
 9482                    }
 9483
 9484                    if let EditorEvent::BufferEdited = event {
 9485                        *follower_edit_event_count.borrow_mut() += 1;
 9486                    }
 9487                },
 9488            )
 9489            .detach();
 9490        }
 9491    });
 9492
 9493    // Update the selections only
 9494    _ = leader.update(cx, |leader, cx| {
 9495        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9496    });
 9497    follower
 9498        .update(cx, |follower, cx| {
 9499            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9500        })
 9501        .unwrap()
 9502        .await
 9503        .unwrap();
 9504    _ = follower.update(cx, |follower, cx| {
 9505        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9506    });
 9507    assert!(*is_still_following.borrow());
 9508    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9509
 9510    // Update the scroll position only
 9511    _ = leader.update(cx, |leader, cx| {
 9512        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9513    });
 9514    follower
 9515        .update(cx, |follower, cx| {
 9516            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9517        })
 9518        .unwrap()
 9519        .await
 9520        .unwrap();
 9521    assert_eq!(
 9522        follower
 9523            .update(cx, |follower, cx| follower.scroll_position(cx))
 9524            .unwrap(),
 9525        gpui::Point::new(1.5, 3.5)
 9526    );
 9527    assert!(*is_still_following.borrow());
 9528    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9529
 9530    // Update the selections and scroll position. The follower's scroll position is updated
 9531    // via autoscroll, not via the leader's exact scroll position.
 9532    _ = leader.update(cx, |leader, cx| {
 9533        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9534        leader.request_autoscroll(Autoscroll::newest(), cx);
 9535        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9536    });
 9537    follower
 9538        .update(cx, |follower, cx| {
 9539            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9540        })
 9541        .unwrap()
 9542        .await
 9543        .unwrap();
 9544    _ = follower.update(cx, |follower, cx| {
 9545        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9546        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9547    });
 9548    assert!(*is_still_following.borrow());
 9549
 9550    // Creating a pending selection that precedes another selection
 9551    _ = leader.update(cx, |leader, cx| {
 9552        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9553        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9554    });
 9555    follower
 9556        .update(cx, |follower, cx| {
 9557            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9558        })
 9559        .unwrap()
 9560        .await
 9561        .unwrap();
 9562    _ = follower.update(cx, |follower, cx| {
 9563        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9564    });
 9565    assert!(*is_still_following.borrow());
 9566
 9567    // Extend the pending selection so that it surrounds another selection
 9568    _ = leader.update(cx, |leader, cx| {
 9569        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9570    });
 9571    follower
 9572        .update(cx, |follower, cx| {
 9573            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9574        })
 9575        .unwrap()
 9576        .await
 9577        .unwrap();
 9578    _ = follower.update(cx, |follower, cx| {
 9579        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9580    });
 9581
 9582    // Scrolling locally breaks the follow
 9583    _ = follower.update(cx, |follower, cx| {
 9584        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9585        follower.set_scroll_anchor(
 9586            ScrollAnchor {
 9587                anchor: top_anchor,
 9588                offset: gpui::Point::new(0.0, 0.5),
 9589            },
 9590            cx,
 9591        );
 9592    });
 9593    assert!(!(*is_still_following.borrow()));
 9594}
 9595
 9596#[gpui::test]
 9597async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9598    init_test(cx, |_| {});
 9599
 9600    let fs = FakeFs::new(cx.executor());
 9601    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9602    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9603    let pane = workspace
 9604        .update(cx, |workspace, _| workspace.active_pane().clone())
 9605        .unwrap();
 9606
 9607    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9608
 9609    let leader = pane.update(cx, |_, cx| {
 9610        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9611        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9612    });
 9613
 9614    // Start following the editor when it has no excerpts.
 9615    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9616    let follower_1 = cx
 9617        .update_window(*workspace.deref(), |_, cx| {
 9618            Editor::from_state_proto(
 9619                workspace.root_view(cx).unwrap(),
 9620                ViewId {
 9621                    creator: Default::default(),
 9622                    id: 0,
 9623                },
 9624                &mut state_message,
 9625                cx,
 9626            )
 9627        })
 9628        .unwrap()
 9629        .unwrap()
 9630        .await
 9631        .unwrap();
 9632
 9633    let update_message = Rc::new(RefCell::new(None));
 9634    follower_1.update(cx, {
 9635        let update = update_message.clone();
 9636        |_, cx| {
 9637            cx.subscribe(&leader, move |_, leader, event, cx| {
 9638                leader
 9639                    .read(cx)
 9640                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9641            })
 9642            .detach();
 9643        }
 9644    });
 9645
 9646    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9647        (
 9648            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9649            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9650        )
 9651    });
 9652
 9653    // Insert some excerpts.
 9654    leader.update(cx, |leader, cx| {
 9655        leader.buffer.update(cx, |multibuffer, cx| {
 9656            let excerpt_ids = multibuffer.push_excerpts(
 9657                buffer_1.clone(),
 9658                [
 9659                    ExcerptRange {
 9660                        context: 1..6,
 9661                        primary: None,
 9662                    },
 9663                    ExcerptRange {
 9664                        context: 12..15,
 9665                        primary: None,
 9666                    },
 9667                    ExcerptRange {
 9668                        context: 0..3,
 9669                        primary: None,
 9670                    },
 9671                ],
 9672                cx,
 9673            );
 9674            multibuffer.insert_excerpts_after(
 9675                excerpt_ids[0],
 9676                buffer_2.clone(),
 9677                [
 9678                    ExcerptRange {
 9679                        context: 8..12,
 9680                        primary: None,
 9681                    },
 9682                    ExcerptRange {
 9683                        context: 0..6,
 9684                        primary: None,
 9685                    },
 9686                ],
 9687                cx,
 9688            );
 9689        });
 9690    });
 9691
 9692    // Apply the update of adding the excerpts.
 9693    follower_1
 9694        .update(cx, |follower, cx| {
 9695            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9696        })
 9697        .await
 9698        .unwrap();
 9699    assert_eq!(
 9700        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9701        leader.update(cx, |editor, cx| editor.text(cx))
 9702    );
 9703    update_message.borrow_mut().take();
 9704
 9705    // Start following separately after it already has excerpts.
 9706    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9707    let follower_2 = cx
 9708        .update_window(*workspace.deref(), |_, cx| {
 9709            Editor::from_state_proto(
 9710                workspace.root_view(cx).unwrap().clone(),
 9711                ViewId {
 9712                    creator: Default::default(),
 9713                    id: 0,
 9714                },
 9715                &mut state_message,
 9716                cx,
 9717            )
 9718        })
 9719        .unwrap()
 9720        .unwrap()
 9721        .await
 9722        .unwrap();
 9723    assert_eq!(
 9724        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9725        leader.update(cx, |editor, cx| editor.text(cx))
 9726    );
 9727
 9728    // Remove some excerpts.
 9729    leader.update(cx, |leader, cx| {
 9730        leader.buffer.update(cx, |multibuffer, cx| {
 9731            let excerpt_ids = multibuffer.excerpt_ids();
 9732            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9733            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9734        });
 9735    });
 9736
 9737    // Apply the update of removing the excerpts.
 9738    follower_1
 9739        .update(cx, |follower, cx| {
 9740            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9741        })
 9742        .await
 9743        .unwrap();
 9744    follower_2
 9745        .update(cx, |follower, cx| {
 9746            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9747        })
 9748        .await
 9749        .unwrap();
 9750    update_message.borrow_mut().take();
 9751    assert_eq!(
 9752        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9753        leader.update(cx, |editor, cx| editor.text(cx))
 9754    );
 9755}
 9756
 9757#[gpui::test]
 9758async fn go_to_prev_overlapping_diagnostic(
 9759    executor: BackgroundExecutor,
 9760    cx: &mut gpui::TestAppContext,
 9761) {
 9762    init_test(cx, |_| {});
 9763
 9764    let mut cx = EditorTestContext::new(cx).await;
 9765    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9766
 9767    cx.set_state(indoc! {"
 9768        ˇfn func(abc def: i32) -> u32 {
 9769        }
 9770    "});
 9771
 9772    cx.update(|cx| {
 9773        project.update(cx, |project, cx| {
 9774            project
 9775                .update_diagnostics(
 9776                    LanguageServerId(0),
 9777                    lsp::PublishDiagnosticsParams {
 9778                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9779                        version: None,
 9780                        diagnostics: vec![
 9781                            lsp::Diagnostic {
 9782                                range: lsp::Range::new(
 9783                                    lsp::Position::new(0, 11),
 9784                                    lsp::Position::new(0, 12),
 9785                                ),
 9786                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9787                                ..Default::default()
 9788                            },
 9789                            lsp::Diagnostic {
 9790                                range: lsp::Range::new(
 9791                                    lsp::Position::new(0, 12),
 9792                                    lsp::Position::new(0, 15),
 9793                                ),
 9794                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9795                                ..Default::default()
 9796                            },
 9797                            lsp::Diagnostic {
 9798                                range: lsp::Range::new(
 9799                                    lsp::Position::new(0, 25),
 9800                                    lsp::Position::new(0, 28),
 9801                                ),
 9802                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9803                                ..Default::default()
 9804                            },
 9805                        ],
 9806                    },
 9807                    &[],
 9808                    cx,
 9809                )
 9810                .unwrap()
 9811        });
 9812    });
 9813
 9814    executor.run_until_parked();
 9815
 9816    cx.update_editor(|editor, cx| {
 9817        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9818    });
 9819
 9820    cx.assert_editor_state(indoc! {"
 9821        fn func(abc def: i32) -> ˇu32 {
 9822        }
 9823    "});
 9824
 9825    cx.update_editor(|editor, cx| {
 9826        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9827    });
 9828
 9829    cx.assert_editor_state(indoc! {"
 9830        fn func(abc ˇdef: i32) -> u32 {
 9831        }
 9832    "});
 9833
 9834    cx.update_editor(|editor, cx| {
 9835        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9836    });
 9837
 9838    cx.assert_editor_state(indoc! {"
 9839        fn func(abcˇ def: i32) -> u32 {
 9840        }
 9841    "});
 9842
 9843    cx.update_editor(|editor, cx| {
 9844        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9845    });
 9846
 9847    cx.assert_editor_state(indoc! {"
 9848        fn func(abc def: i32) -> ˇu32 {
 9849        }
 9850    "});
 9851}
 9852
 9853#[gpui::test]
 9854async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9855    init_test(cx, |_| {});
 9856
 9857    let mut cx = EditorTestContext::new(cx).await;
 9858
 9859    cx.set_state(indoc! {"
 9860        fn func(abˇc def: i32) -> u32 {
 9861        }
 9862    "});
 9863    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9864
 9865    cx.update(|cx| {
 9866        project.update(cx, |project, cx| {
 9867            project.update_diagnostics(
 9868                LanguageServerId(0),
 9869                lsp::PublishDiagnosticsParams {
 9870                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9871                    version: None,
 9872                    diagnostics: vec![lsp::Diagnostic {
 9873                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9874                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9875                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9876                        ..Default::default()
 9877                    }],
 9878                },
 9879                &[],
 9880                cx,
 9881            )
 9882        })
 9883    }).unwrap();
 9884    cx.run_until_parked();
 9885    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9886    cx.run_until_parked();
 9887    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9888}
 9889
 9890#[gpui::test]
 9891async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9892    init_test(cx, |_| {});
 9893
 9894    let mut cx = EditorTestContext::new(cx).await;
 9895
 9896    let diff_base = r#"
 9897        use some::mod;
 9898
 9899        const A: u32 = 42;
 9900
 9901        fn main() {
 9902            println!("hello");
 9903
 9904            println!("world");
 9905        }
 9906        "#
 9907    .unindent();
 9908
 9909    // Edits are modified, removed, modified, added
 9910    cx.set_state(
 9911        &r#"
 9912        use some::modified;
 9913
 9914        ˇ
 9915        fn main() {
 9916            println!("hello there");
 9917
 9918            println!("around the");
 9919            println!("world");
 9920        }
 9921        "#
 9922        .unindent(),
 9923    );
 9924
 9925    cx.set_diff_base(Some(&diff_base));
 9926    executor.run_until_parked();
 9927
 9928    cx.update_editor(|editor, cx| {
 9929        //Wrap around the bottom of the buffer
 9930        for _ in 0..3 {
 9931            editor.go_to_next_hunk(&GoToHunk, cx);
 9932        }
 9933    });
 9934
 9935    cx.assert_editor_state(
 9936        &r#"
 9937        ˇuse some::modified;
 9938
 9939
 9940        fn main() {
 9941            println!("hello there");
 9942
 9943            println!("around the");
 9944            println!("world");
 9945        }
 9946        "#
 9947        .unindent(),
 9948    );
 9949
 9950    cx.update_editor(|editor, cx| {
 9951        //Wrap around the top of the buffer
 9952        for _ in 0..2 {
 9953            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9954        }
 9955    });
 9956
 9957    cx.assert_editor_state(
 9958        &r#"
 9959        use some::modified;
 9960
 9961
 9962        fn main() {
 9963        ˇ    println!("hello there");
 9964
 9965            println!("around the");
 9966            println!("world");
 9967        }
 9968        "#
 9969        .unindent(),
 9970    );
 9971
 9972    cx.update_editor(|editor, cx| {
 9973        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9974    });
 9975
 9976    cx.assert_editor_state(
 9977        &r#"
 9978        use some::modified;
 9979
 9980        ˇ
 9981        fn main() {
 9982            println!("hello there");
 9983
 9984            println!("around the");
 9985            println!("world");
 9986        }
 9987        "#
 9988        .unindent(),
 9989    );
 9990
 9991    cx.update_editor(|editor, cx| {
 9992        for _ in 0..3 {
 9993            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9994        }
 9995    });
 9996
 9997    cx.assert_editor_state(
 9998        &r#"
 9999        use some::modified;
10000
10001
10002        fn main() {
10003        ˇ    println!("hello there");
10004
10005            println!("around the");
10006            println!("world");
10007        }
10008        "#
10009        .unindent(),
10010    );
10011
10012    cx.update_editor(|editor, cx| {
10013        editor.fold(&Fold, cx);
10014
10015        //Make sure that the fold only gets one hunk
10016        for _ in 0..4 {
10017            editor.go_to_next_hunk(&GoToHunk, cx);
10018        }
10019    });
10020
10021    cx.assert_editor_state(
10022        &r#"
10023        ˇuse some::modified;
10024
10025
10026        fn main() {
10027            println!("hello there");
10028
10029            println!("around the");
10030            println!("world");
10031        }
10032        "#
10033        .unindent(),
10034    );
10035}
10036
10037#[test]
10038fn test_split_words() {
10039    fn split(text: &str) -> Vec<&str> {
10040        split_words(text).collect()
10041    }
10042
10043    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10044    assert_eq!(split("hello_world"), &["hello_", "world"]);
10045    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10046    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10047    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10048    assert_eq!(split("helloworld"), &["helloworld"]);
10049
10050    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10051}
10052
10053#[gpui::test]
10054async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10055    init_test(cx, |_| {});
10056
10057    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10058    let mut assert = |before, after| {
10059        let _state_context = cx.set_state(before);
10060        cx.update_editor(|editor, cx| {
10061            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10062        });
10063        cx.assert_editor_state(after);
10064    };
10065
10066    // Outside bracket jumps to outside of matching bracket
10067    assert("console.logˇ(var);", "console.log(var)ˇ;");
10068    assert("console.log(var)ˇ;", "console.logˇ(var);");
10069
10070    // Inside bracket jumps to inside of matching bracket
10071    assert("console.log(ˇvar);", "console.log(varˇ);");
10072    assert("console.log(varˇ);", "console.log(ˇvar);");
10073
10074    // When outside a bracket and inside, favor jumping to the inside bracket
10075    assert(
10076        "console.log('foo', [1, 2, 3]ˇ);",
10077        "console.log(ˇ'foo', [1, 2, 3]);",
10078    );
10079    assert(
10080        "console.log(ˇ'foo', [1, 2, 3]);",
10081        "console.log('foo', [1, 2, 3]ˇ);",
10082    );
10083
10084    // Bias forward if two options are equally likely
10085    assert(
10086        "let result = curried_fun()ˇ();",
10087        "let result = curried_fun()()ˇ;",
10088    );
10089
10090    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10091    assert(
10092        indoc! {"
10093            function test() {
10094                console.log('test')ˇ
10095            }"},
10096        indoc! {"
10097            function test() {
10098                console.logˇ('test')
10099            }"},
10100    );
10101}
10102
10103#[gpui::test]
10104async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10105    init_test(cx, |_| {});
10106
10107    let fs = FakeFs::new(cx.executor());
10108    fs.insert_tree(
10109        "/a",
10110        json!({
10111            "main.rs": "fn main() { let a = 5; }",
10112            "other.rs": "// Test file",
10113        }),
10114    )
10115    .await;
10116    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10117
10118    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10119    language_registry.add(Arc::new(Language::new(
10120        LanguageConfig {
10121            name: "Rust".into(),
10122            matcher: LanguageMatcher {
10123                path_suffixes: vec!["rs".to_string()],
10124                ..Default::default()
10125            },
10126            brackets: BracketPairConfig {
10127                pairs: vec![BracketPair {
10128                    start: "{".to_string(),
10129                    end: "}".to_string(),
10130                    close: true,
10131                    surround: true,
10132                    newline: true,
10133                }],
10134                disabled_scopes_by_bracket_ix: Vec::new(),
10135            },
10136            ..Default::default()
10137        },
10138        Some(tree_sitter_rust::LANGUAGE.into()),
10139    )));
10140    let mut fake_servers = language_registry.register_fake_lsp(
10141        "Rust",
10142        FakeLspAdapter {
10143            capabilities: lsp::ServerCapabilities {
10144                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10145                    first_trigger_character: "{".to_string(),
10146                    more_trigger_character: None,
10147                }),
10148                ..Default::default()
10149            },
10150            ..Default::default()
10151        },
10152    );
10153
10154    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10155
10156    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10157
10158    let worktree_id = workspace
10159        .update(cx, |workspace, cx| {
10160            workspace.project().update(cx, |project, cx| {
10161                project.worktrees(cx).next().unwrap().read(cx).id()
10162            })
10163        })
10164        .unwrap();
10165
10166    let buffer = project
10167        .update(cx, |project, cx| {
10168            project.open_local_buffer("/a/main.rs", cx)
10169        })
10170        .await
10171        .unwrap();
10172    cx.executor().run_until_parked();
10173    cx.executor().start_waiting();
10174    let fake_server = fake_servers.next().await.unwrap();
10175    let editor_handle = workspace
10176        .update(cx, |workspace, cx| {
10177            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10178        })
10179        .unwrap()
10180        .await
10181        .unwrap()
10182        .downcast::<Editor>()
10183        .unwrap();
10184
10185    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10186        assert_eq!(
10187            params.text_document_position.text_document.uri,
10188            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10189        );
10190        assert_eq!(
10191            params.text_document_position.position,
10192            lsp::Position::new(0, 21),
10193        );
10194
10195        Ok(Some(vec![lsp::TextEdit {
10196            new_text: "]".to_string(),
10197            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10198        }]))
10199    });
10200
10201    editor_handle.update(cx, |editor, cx| {
10202        editor.focus(cx);
10203        editor.change_selections(None, cx, |s| {
10204            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10205        });
10206        editor.handle_input("{", cx);
10207    });
10208
10209    cx.executor().run_until_parked();
10210
10211    buffer.update(cx, |buffer, _| {
10212        assert_eq!(
10213            buffer.text(),
10214            "fn main() { let a = {5}; }",
10215            "No extra braces from on type formatting should appear in the buffer"
10216        )
10217    });
10218}
10219
10220#[gpui::test]
10221async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10222    init_test(cx, |_| {});
10223
10224    let fs = FakeFs::new(cx.executor());
10225    fs.insert_tree(
10226        "/a",
10227        json!({
10228            "main.rs": "fn main() { let a = 5; }",
10229            "other.rs": "// Test file",
10230        }),
10231    )
10232    .await;
10233
10234    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10235
10236    let server_restarts = Arc::new(AtomicUsize::new(0));
10237    let closure_restarts = Arc::clone(&server_restarts);
10238    let language_server_name = "test language server";
10239    let language_name: LanguageName = "Rust".into();
10240
10241    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10242    language_registry.add(Arc::new(Language::new(
10243        LanguageConfig {
10244            name: language_name.clone(),
10245            matcher: LanguageMatcher {
10246                path_suffixes: vec!["rs".to_string()],
10247                ..Default::default()
10248            },
10249            ..Default::default()
10250        },
10251        Some(tree_sitter_rust::LANGUAGE.into()),
10252    )));
10253    let mut fake_servers = language_registry.register_fake_lsp(
10254        "Rust",
10255        FakeLspAdapter {
10256            name: language_server_name,
10257            initialization_options: Some(json!({
10258                "testOptionValue": true
10259            })),
10260            initializer: Some(Box::new(move |fake_server| {
10261                let task_restarts = Arc::clone(&closure_restarts);
10262                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10263                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10264                    futures::future::ready(Ok(()))
10265                });
10266            })),
10267            ..Default::default()
10268        },
10269    );
10270
10271    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10272    let _buffer = project
10273        .update(cx, |project, cx| {
10274            project.open_local_buffer("/a/main.rs", cx)
10275        })
10276        .await
10277        .unwrap();
10278    let _fake_server = fake_servers.next().await.unwrap();
10279    update_test_language_settings(cx, |language_settings| {
10280        language_settings.languages.insert(
10281            language_name.clone(),
10282            LanguageSettingsContent {
10283                tab_size: NonZeroU32::new(8),
10284                ..Default::default()
10285            },
10286        );
10287    });
10288    cx.executor().run_until_parked();
10289    assert_eq!(
10290        server_restarts.load(atomic::Ordering::Acquire),
10291        0,
10292        "Should not restart LSP server on an unrelated change"
10293    );
10294
10295    update_test_project_settings(cx, |project_settings| {
10296        project_settings.lsp.insert(
10297            "Some other server name".into(),
10298            LspSettings {
10299                binary: None,
10300                settings: None,
10301                initialization_options: Some(json!({
10302                    "some other init value": false
10303                })),
10304            },
10305        );
10306    });
10307    cx.executor().run_until_parked();
10308    assert_eq!(
10309        server_restarts.load(atomic::Ordering::Acquire),
10310        0,
10311        "Should not restart LSP server on an unrelated LSP settings change"
10312    );
10313
10314    update_test_project_settings(cx, |project_settings| {
10315        project_settings.lsp.insert(
10316            language_server_name.into(),
10317            LspSettings {
10318                binary: None,
10319                settings: None,
10320                initialization_options: Some(json!({
10321                    "anotherInitValue": false
10322                })),
10323            },
10324        );
10325    });
10326    cx.executor().run_until_parked();
10327    assert_eq!(
10328        server_restarts.load(atomic::Ordering::Acquire),
10329        1,
10330        "Should restart LSP server on a related LSP settings change"
10331    );
10332
10333    update_test_project_settings(cx, |project_settings| {
10334        project_settings.lsp.insert(
10335            language_server_name.into(),
10336            LspSettings {
10337                binary: None,
10338                settings: None,
10339                initialization_options: Some(json!({
10340                    "anotherInitValue": false
10341                })),
10342            },
10343        );
10344    });
10345    cx.executor().run_until_parked();
10346    assert_eq!(
10347        server_restarts.load(atomic::Ordering::Acquire),
10348        1,
10349        "Should not restart LSP server on a related LSP settings change that is the same"
10350    );
10351
10352    update_test_project_settings(cx, |project_settings| {
10353        project_settings.lsp.insert(
10354            language_server_name.into(),
10355            LspSettings {
10356                binary: None,
10357                settings: None,
10358                initialization_options: None,
10359            },
10360        );
10361    });
10362    cx.executor().run_until_parked();
10363    assert_eq!(
10364        server_restarts.load(atomic::Ordering::Acquire),
10365        2,
10366        "Should restart LSP server on another related LSP settings change"
10367    );
10368}
10369
10370#[gpui::test]
10371async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10372    init_test(cx, |_| {});
10373
10374    let mut cx = EditorLspTestContext::new_rust(
10375        lsp::ServerCapabilities {
10376            completion_provider: Some(lsp::CompletionOptions {
10377                trigger_characters: Some(vec![".".to_string()]),
10378                resolve_provider: Some(true),
10379                ..Default::default()
10380            }),
10381            ..Default::default()
10382        },
10383        cx,
10384    )
10385    .await;
10386
10387    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10388    cx.simulate_keystroke(".");
10389    let completion_item = lsp::CompletionItem {
10390        label: "some".into(),
10391        kind: Some(lsp::CompletionItemKind::SNIPPET),
10392        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10393        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10394            kind: lsp::MarkupKind::Markdown,
10395            value: "```rust\nSome(2)\n```".to_string(),
10396        })),
10397        deprecated: Some(false),
10398        sort_text: Some("fffffff2".to_string()),
10399        filter_text: Some("some".to_string()),
10400        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10401        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10402            range: lsp::Range {
10403                start: lsp::Position {
10404                    line: 0,
10405                    character: 22,
10406                },
10407                end: lsp::Position {
10408                    line: 0,
10409                    character: 22,
10410                },
10411            },
10412            new_text: "Some(2)".to_string(),
10413        })),
10414        additional_text_edits: Some(vec![lsp::TextEdit {
10415            range: lsp::Range {
10416                start: lsp::Position {
10417                    line: 0,
10418                    character: 20,
10419                },
10420                end: lsp::Position {
10421                    line: 0,
10422                    character: 22,
10423                },
10424            },
10425            new_text: "".to_string(),
10426        }]),
10427        ..Default::default()
10428    };
10429
10430    let closure_completion_item = completion_item.clone();
10431    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10432        let task_completion_item = closure_completion_item.clone();
10433        async move {
10434            Ok(Some(lsp::CompletionResponse::Array(vec![
10435                task_completion_item,
10436            ])))
10437        }
10438    });
10439
10440    request.next().await;
10441
10442    cx.condition(|editor, _| editor.context_menu_visible())
10443        .await;
10444    let apply_additional_edits = cx.update_editor(|editor, cx| {
10445        editor
10446            .confirm_completion(&ConfirmCompletion::default(), cx)
10447            .unwrap()
10448    });
10449    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10450
10451    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10452        let task_completion_item = completion_item.clone();
10453        async move { Ok(task_completion_item) }
10454    })
10455    .next()
10456    .await
10457    .unwrap();
10458    apply_additional_edits.await.unwrap();
10459    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10460}
10461
10462#[gpui::test]
10463async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10464    init_test(cx, |_| {});
10465
10466    let mut cx = EditorLspTestContext::new(
10467        Language::new(
10468            LanguageConfig {
10469                matcher: LanguageMatcher {
10470                    path_suffixes: vec!["jsx".into()],
10471                    ..Default::default()
10472                },
10473                overrides: [(
10474                    "element".into(),
10475                    LanguageConfigOverride {
10476                        word_characters: Override::Set(['-'].into_iter().collect()),
10477                        ..Default::default()
10478                    },
10479                )]
10480                .into_iter()
10481                .collect(),
10482                ..Default::default()
10483            },
10484            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10485        )
10486        .with_override_query("(jsx_self_closing_element) @element")
10487        .unwrap(),
10488        lsp::ServerCapabilities {
10489            completion_provider: Some(lsp::CompletionOptions {
10490                trigger_characters: Some(vec![":".to_string()]),
10491                ..Default::default()
10492            }),
10493            ..Default::default()
10494        },
10495        cx,
10496    )
10497    .await;
10498
10499    cx.lsp
10500        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10501            Ok(Some(lsp::CompletionResponse::Array(vec![
10502                lsp::CompletionItem {
10503                    label: "bg-blue".into(),
10504                    ..Default::default()
10505                },
10506                lsp::CompletionItem {
10507                    label: "bg-red".into(),
10508                    ..Default::default()
10509                },
10510                lsp::CompletionItem {
10511                    label: "bg-yellow".into(),
10512                    ..Default::default()
10513                },
10514            ])))
10515        });
10516
10517    cx.set_state(r#"<p class="bgˇ" />"#);
10518
10519    // Trigger completion when typing a dash, because the dash is an extra
10520    // word character in the 'element' scope, which contains the cursor.
10521    cx.simulate_keystroke("-");
10522    cx.executor().run_until_parked();
10523    cx.update_editor(|editor, _| {
10524        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10525            assert_eq!(
10526                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10527                &["bg-red", "bg-blue", "bg-yellow"]
10528            );
10529        } else {
10530            panic!("expected completion menu to be open");
10531        }
10532    });
10533
10534    cx.simulate_keystroke("l");
10535    cx.executor().run_until_parked();
10536    cx.update_editor(|editor, _| {
10537        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10538            assert_eq!(
10539                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10540                &["bg-blue", "bg-yellow"]
10541            );
10542        } else {
10543            panic!("expected completion menu to be open");
10544        }
10545    });
10546
10547    // When filtering completions, consider the character after the '-' to
10548    // be the start of a subword.
10549    cx.set_state(r#"<p class="yelˇ" />"#);
10550    cx.simulate_keystroke("l");
10551    cx.executor().run_until_parked();
10552    cx.update_editor(|editor, _| {
10553        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10554            assert_eq!(
10555                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10556                &["bg-yellow"]
10557            );
10558        } else {
10559            panic!("expected completion menu to be open");
10560        }
10561    });
10562}
10563
10564#[gpui::test]
10565async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10566    init_test(cx, |settings| {
10567        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10568            FormatterList(vec![Formatter::Prettier].into()),
10569        ))
10570    });
10571
10572    let fs = FakeFs::new(cx.executor());
10573    fs.insert_file("/file.ts", Default::default()).await;
10574
10575    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10576    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10577
10578    language_registry.add(Arc::new(Language::new(
10579        LanguageConfig {
10580            name: "TypeScript".into(),
10581            matcher: LanguageMatcher {
10582                path_suffixes: vec!["ts".to_string()],
10583                ..Default::default()
10584            },
10585            ..Default::default()
10586        },
10587        Some(tree_sitter_rust::LANGUAGE.into()),
10588    )));
10589    update_test_language_settings(cx, |settings| {
10590        settings.defaults.prettier = Some(PrettierSettings {
10591            allowed: true,
10592            ..PrettierSettings::default()
10593        });
10594    });
10595
10596    let test_plugin = "test_plugin";
10597    let _ = language_registry.register_fake_lsp(
10598        "TypeScript",
10599        FakeLspAdapter {
10600            prettier_plugins: vec![test_plugin],
10601            ..Default::default()
10602        },
10603    );
10604
10605    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10606    let buffer = project
10607        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10608        .await
10609        .unwrap();
10610
10611    let buffer_text = "one\ntwo\nthree\n";
10612    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10613    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10614    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10615
10616    editor
10617        .update(cx, |editor, cx| {
10618            editor.perform_format(
10619                project.clone(),
10620                FormatTrigger::Manual,
10621                FormatTarget::Buffer,
10622                cx,
10623            )
10624        })
10625        .unwrap()
10626        .await;
10627    assert_eq!(
10628        editor.update(cx, |editor, cx| editor.text(cx)),
10629        buffer_text.to_string() + prettier_format_suffix,
10630        "Test prettier formatting was not applied to the original buffer text",
10631    );
10632
10633    update_test_language_settings(cx, |settings| {
10634        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10635    });
10636    let format = editor.update(cx, |editor, cx| {
10637        editor.perform_format(
10638            project.clone(),
10639            FormatTrigger::Manual,
10640            FormatTarget::Buffer,
10641            cx,
10642        )
10643    });
10644    format.await.unwrap();
10645    assert_eq!(
10646        editor.update(cx, |editor, cx| editor.text(cx)),
10647        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10648        "Autoformatting (via test prettier) was not applied to the original buffer text",
10649    );
10650}
10651
10652#[gpui::test]
10653async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10654    init_test(cx, |_| {});
10655    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10656    let base_text = indoc! {r#"struct Row;
10657struct Row1;
10658struct Row2;
10659
10660struct Row4;
10661struct Row5;
10662struct Row6;
10663
10664struct Row8;
10665struct Row9;
10666struct Row10;"#};
10667
10668    // When addition hunks are not adjacent to carets, no hunk revert is performed
10669    assert_hunk_revert(
10670        indoc! {r#"struct Row;
10671                   struct Row1;
10672                   struct Row1.1;
10673                   struct Row1.2;
10674                   struct Row2;ˇ
10675
10676                   struct Row4;
10677                   struct Row5;
10678                   struct Row6;
10679
10680                   struct Row8;
10681                   ˇstruct Row9;
10682                   struct Row9.1;
10683                   struct Row9.2;
10684                   struct Row9.3;
10685                   struct Row10;"#},
10686        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10687        indoc! {r#"struct Row;
10688                   struct Row1;
10689                   struct Row1.1;
10690                   struct Row1.2;
10691                   struct Row2;ˇ
10692
10693                   struct Row4;
10694                   struct Row5;
10695                   struct Row6;
10696
10697                   struct Row8;
10698                   ˇstruct Row9;
10699                   struct Row9.1;
10700                   struct Row9.2;
10701                   struct Row9.3;
10702                   struct Row10;"#},
10703        base_text,
10704        &mut cx,
10705    );
10706    // Same for selections
10707    assert_hunk_revert(
10708        indoc! {r#"struct Row;
10709                   struct Row1;
10710                   struct Row2;
10711                   struct Row2.1;
10712                   struct Row2.2;
10713                   «ˇ
10714                   struct Row4;
10715                   struct» Row5;
10716                   «struct Row6;
10717                   ˇ»
10718                   struct Row9.1;
10719                   struct Row9.2;
10720                   struct Row9.3;
10721                   struct Row8;
10722                   struct Row9;
10723                   struct Row10;"#},
10724        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10725        indoc! {r#"struct Row;
10726                   struct Row1;
10727                   struct Row2;
10728                   struct Row2.1;
10729                   struct Row2.2;
10730                   «ˇ
10731                   struct Row4;
10732                   struct» Row5;
10733                   «struct Row6;
10734                   ˇ»
10735                   struct Row9.1;
10736                   struct Row9.2;
10737                   struct Row9.3;
10738                   struct Row8;
10739                   struct Row9;
10740                   struct Row10;"#},
10741        base_text,
10742        &mut cx,
10743    );
10744
10745    // When carets and selections intersect the addition hunks, those are reverted.
10746    // Adjacent carets got merged.
10747    assert_hunk_revert(
10748        indoc! {r#"struct Row;
10749                   ˇ// something on the top
10750                   struct Row1;
10751                   struct Row2;
10752                   struct Roˇw3.1;
10753                   struct Row2.2;
10754                   struct Row2.3;ˇ
10755
10756                   struct Row4;
10757                   struct ˇRow5.1;
10758                   struct Row5.2;
10759                   struct «Rowˇ»5.3;
10760                   struct Row5;
10761                   struct Row6;
10762                   ˇ
10763                   struct Row9.1;
10764                   struct «Rowˇ»9.2;
10765                   struct «ˇRow»9.3;
10766                   struct Row8;
10767                   struct Row9;
10768                   «ˇ// something on bottom»
10769                   struct Row10;"#},
10770        vec![
10771            DiffHunkStatus::Added,
10772            DiffHunkStatus::Added,
10773            DiffHunkStatus::Added,
10774            DiffHunkStatus::Added,
10775            DiffHunkStatus::Added,
10776        ],
10777        indoc! {r#"struct Row;
10778                   ˇstruct Row1;
10779                   struct Row2;
10780                   ˇ
10781                   struct Row4;
10782                   ˇstruct Row5;
10783                   struct Row6;
10784                   ˇ
10785                   ˇstruct Row8;
10786                   struct Row9;
10787                   ˇstruct Row10;"#},
10788        base_text,
10789        &mut cx,
10790    );
10791}
10792
10793#[gpui::test]
10794async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10795    init_test(cx, |_| {});
10796    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10797    let base_text = indoc! {r#"struct Row;
10798struct Row1;
10799struct Row2;
10800
10801struct Row4;
10802struct Row5;
10803struct Row6;
10804
10805struct Row8;
10806struct Row9;
10807struct Row10;"#};
10808
10809    // Modification hunks behave the same as the addition ones.
10810    assert_hunk_revert(
10811        indoc! {r#"struct Row;
10812                   struct Row1;
10813                   struct Row33;
10814                   ˇ
10815                   struct Row4;
10816                   struct Row5;
10817                   struct Row6;
10818                   ˇ
10819                   struct Row99;
10820                   struct Row9;
10821                   struct Row10;"#},
10822        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10823        indoc! {r#"struct Row;
10824                   struct Row1;
10825                   struct Row33;
10826                   ˇ
10827                   struct Row4;
10828                   struct Row5;
10829                   struct Row6;
10830                   ˇ
10831                   struct Row99;
10832                   struct Row9;
10833                   struct Row10;"#},
10834        base_text,
10835        &mut cx,
10836    );
10837    assert_hunk_revert(
10838        indoc! {r#"struct Row;
10839                   struct Row1;
10840                   struct Row33;
10841                   «ˇ
10842                   struct Row4;
10843                   struct» Row5;
10844                   «struct Row6;
10845                   ˇ»
10846                   struct Row99;
10847                   struct Row9;
10848                   struct Row10;"#},
10849        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10850        indoc! {r#"struct Row;
10851                   struct Row1;
10852                   struct Row33;
10853                   «ˇ
10854                   struct Row4;
10855                   struct» Row5;
10856                   «struct Row6;
10857                   ˇ»
10858                   struct Row99;
10859                   struct Row9;
10860                   struct Row10;"#},
10861        base_text,
10862        &mut cx,
10863    );
10864
10865    assert_hunk_revert(
10866        indoc! {r#"ˇstruct Row1.1;
10867                   struct Row1;
10868                   «ˇstr»uct Row22;
10869
10870                   struct ˇRow44;
10871                   struct Row5;
10872                   struct «Rˇ»ow66;ˇ
10873
10874                   «struˇ»ct Row88;
10875                   struct Row9;
10876                   struct Row1011;ˇ"#},
10877        vec![
10878            DiffHunkStatus::Modified,
10879            DiffHunkStatus::Modified,
10880            DiffHunkStatus::Modified,
10881            DiffHunkStatus::Modified,
10882            DiffHunkStatus::Modified,
10883            DiffHunkStatus::Modified,
10884        ],
10885        indoc! {r#"struct Row;
10886                   ˇstruct Row1;
10887                   struct Row2;
10888                   ˇ
10889                   struct Row4;
10890                   ˇstruct Row5;
10891                   struct Row6;
10892                   ˇ
10893                   struct Row8;
10894                   ˇstruct Row9;
10895                   struct Row10;ˇ"#},
10896        base_text,
10897        &mut cx,
10898    );
10899}
10900
10901#[gpui::test]
10902async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10903    init_test(cx, |_| {});
10904    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10905    let base_text = indoc! {r#"struct Row;
10906struct Row1;
10907struct Row2;
10908
10909struct Row4;
10910struct Row5;
10911struct Row6;
10912
10913struct Row8;
10914struct Row9;
10915struct Row10;"#};
10916
10917    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10918    assert_hunk_revert(
10919        indoc! {r#"struct Row;
10920                   struct Row2;
10921
10922                   ˇstruct Row4;
10923                   struct Row5;
10924                   struct Row6;
10925                   ˇ
10926                   struct Row8;
10927                   struct Row10;"#},
10928        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10929        indoc! {r#"struct Row;
10930                   struct Row2;
10931
10932                   ˇstruct Row4;
10933                   struct Row5;
10934                   struct Row6;
10935                   ˇ
10936                   struct Row8;
10937                   struct Row10;"#},
10938        base_text,
10939        &mut cx,
10940    );
10941    assert_hunk_revert(
10942        indoc! {r#"struct Row;
10943                   struct Row2;
10944
10945                   «ˇstruct Row4;
10946                   struct» Row5;
10947                   «struct Row6;
10948                   ˇ»
10949                   struct Row8;
10950                   struct Row10;"#},
10951        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10952        indoc! {r#"struct Row;
10953                   struct Row2;
10954
10955                   «ˇstruct Row4;
10956                   struct» Row5;
10957                   «struct Row6;
10958                   ˇ»
10959                   struct Row8;
10960                   struct Row10;"#},
10961        base_text,
10962        &mut cx,
10963    );
10964
10965    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10966    assert_hunk_revert(
10967        indoc! {r#"struct Row;
10968                   ˇstruct Row2;
10969
10970                   struct Row4;
10971                   struct Row5;
10972                   struct Row6;
10973
10974                   struct Row8;ˇ
10975                   struct Row10;"#},
10976        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10977        indoc! {r#"struct Row;
10978                   struct Row1;
10979                   ˇstruct Row2;
10980
10981                   struct Row4;
10982                   struct Row5;
10983                   struct Row6;
10984
10985                   struct Row8;ˇ
10986                   struct Row9;
10987                   struct Row10;"#},
10988        base_text,
10989        &mut cx,
10990    );
10991    assert_hunk_revert(
10992        indoc! {r#"struct Row;
10993                   struct Row2«ˇ;
10994                   struct Row4;
10995                   struct» Row5;
10996                   «struct Row6;
10997
10998                   struct Row8;ˇ»
10999                   struct Row10;"#},
11000        vec![
11001            DiffHunkStatus::Removed,
11002            DiffHunkStatus::Removed,
11003            DiffHunkStatus::Removed,
11004        ],
11005        indoc! {r#"struct Row;
11006                   struct Row1;
11007                   struct Row2«ˇ;
11008
11009                   struct Row4;
11010                   struct» Row5;
11011                   «struct Row6;
11012
11013                   struct Row8;ˇ»
11014                   struct Row9;
11015                   struct Row10;"#},
11016        base_text,
11017        &mut cx,
11018    );
11019}
11020
11021#[gpui::test]
11022async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11023    init_test(cx, |_| {});
11024
11025    let cols = 4;
11026    let rows = 10;
11027    let sample_text_1 = sample_text(rows, cols, 'a');
11028    assert_eq!(
11029        sample_text_1,
11030        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11031    );
11032    let sample_text_2 = sample_text(rows, cols, 'l');
11033    assert_eq!(
11034        sample_text_2,
11035        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11036    );
11037    let sample_text_3 = sample_text(rows, cols, 'v');
11038    assert_eq!(
11039        sample_text_3,
11040        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11041    );
11042
11043    fn diff_every_buffer_row(
11044        buffer: &Model<Buffer>,
11045        sample_text: String,
11046        cols: usize,
11047        cx: &mut gpui::TestAppContext,
11048    ) {
11049        // revert first character in each row, creating one large diff hunk per buffer
11050        let is_first_char = |offset: usize| offset % cols == 0;
11051        buffer.update(cx, |buffer, cx| {
11052            buffer.set_text(
11053                sample_text
11054                    .chars()
11055                    .enumerate()
11056                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11057                    .collect::<String>(),
11058                cx,
11059            );
11060            buffer.set_diff_base(Some(sample_text), cx);
11061        });
11062        cx.executor().run_until_parked();
11063    }
11064
11065    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11066    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11067
11068    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11069    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11070
11071    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11072    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11073
11074    let multibuffer = cx.new_model(|cx| {
11075        let mut multibuffer = MultiBuffer::new(ReadWrite);
11076        multibuffer.push_excerpts(
11077            buffer_1.clone(),
11078            [
11079                ExcerptRange {
11080                    context: Point::new(0, 0)..Point::new(3, 0),
11081                    primary: None,
11082                },
11083                ExcerptRange {
11084                    context: Point::new(5, 0)..Point::new(7, 0),
11085                    primary: None,
11086                },
11087                ExcerptRange {
11088                    context: Point::new(9, 0)..Point::new(10, 4),
11089                    primary: None,
11090                },
11091            ],
11092            cx,
11093        );
11094        multibuffer.push_excerpts(
11095            buffer_2.clone(),
11096            [
11097                ExcerptRange {
11098                    context: Point::new(0, 0)..Point::new(3, 0),
11099                    primary: None,
11100                },
11101                ExcerptRange {
11102                    context: Point::new(5, 0)..Point::new(7, 0),
11103                    primary: None,
11104                },
11105                ExcerptRange {
11106                    context: Point::new(9, 0)..Point::new(10, 4),
11107                    primary: None,
11108                },
11109            ],
11110            cx,
11111        );
11112        multibuffer.push_excerpts(
11113            buffer_3.clone(),
11114            [
11115                ExcerptRange {
11116                    context: Point::new(0, 0)..Point::new(3, 0),
11117                    primary: None,
11118                },
11119                ExcerptRange {
11120                    context: Point::new(5, 0)..Point::new(7, 0),
11121                    primary: None,
11122                },
11123                ExcerptRange {
11124                    context: Point::new(9, 0)..Point::new(10, 4),
11125                    primary: None,
11126                },
11127            ],
11128            cx,
11129        );
11130        multibuffer
11131    });
11132
11133    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11134    editor.update(cx, |editor, cx| {
11135        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
11136        editor.select_all(&SelectAll, cx);
11137        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11138    });
11139    cx.executor().run_until_parked();
11140    // When all ranges are selected, all buffer hunks are reverted.
11141    editor.update(cx, |editor, cx| {
11142        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");
11143    });
11144    buffer_1.update(cx, |buffer, _| {
11145        assert_eq!(buffer.text(), sample_text_1);
11146    });
11147    buffer_2.update(cx, |buffer, _| {
11148        assert_eq!(buffer.text(), sample_text_2);
11149    });
11150    buffer_3.update(cx, |buffer, _| {
11151        assert_eq!(buffer.text(), sample_text_3);
11152    });
11153
11154    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11155    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11156    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11157    editor.update(cx, |editor, cx| {
11158        editor.change_selections(None, cx, |s| {
11159            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11160        });
11161        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11162    });
11163    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11164    // but not affect buffer_2 and its related excerpts.
11165    editor.update(cx, |editor, cx| {
11166        assert_eq!(
11167            editor.text(cx),
11168            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
11169        );
11170    });
11171    buffer_1.update(cx, |buffer, _| {
11172        assert_eq!(buffer.text(), sample_text_1);
11173    });
11174    buffer_2.update(cx, |buffer, _| {
11175        assert_eq!(
11176            buffer.text(),
11177            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11178        );
11179    });
11180    buffer_3.update(cx, |buffer, _| {
11181        assert_eq!(
11182            buffer.text(),
11183            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11184        );
11185    });
11186}
11187
11188#[gpui::test]
11189async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11190    init_test(cx, |_| {});
11191
11192    let cols = 4;
11193    let rows = 10;
11194    let sample_text_1 = sample_text(rows, cols, 'a');
11195    assert_eq!(
11196        sample_text_1,
11197        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11198    );
11199    let sample_text_2 = sample_text(rows, cols, 'l');
11200    assert_eq!(
11201        sample_text_2,
11202        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11203    );
11204    let sample_text_3 = sample_text(rows, cols, 'v');
11205    assert_eq!(
11206        sample_text_3,
11207        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11208    );
11209
11210    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11211    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11212    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11213
11214    let multi_buffer = cx.new_model(|cx| {
11215        let mut multibuffer = MultiBuffer::new(ReadWrite);
11216        multibuffer.push_excerpts(
11217            buffer_1.clone(),
11218            [
11219                ExcerptRange {
11220                    context: Point::new(0, 0)..Point::new(3, 0),
11221                    primary: None,
11222                },
11223                ExcerptRange {
11224                    context: Point::new(5, 0)..Point::new(7, 0),
11225                    primary: None,
11226                },
11227                ExcerptRange {
11228                    context: Point::new(9, 0)..Point::new(10, 4),
11229                    primary: None,
11230                },
11231            ],
11232            cx,
11233        );
11234        multibuffer.push_excerpts(
11235            buffer_2.clone(),
11236            [
11237                ExcerptRange {
11238                    context: Point::new(0, 0)..Point::new(3, 0),
11239                    primary: None,
11240                },
11241                ExcerptRange {
11242                    context: Point::new(5, 0)..Point::new(7, 0),
11243                    primary: None,
11244                },
11245                ExcerptRange {
11246                    context: Point::new(9, 0)..Point::new(10, 4),
11247                    primary: None,
11248                },
11249            ],
11250            cx,
11251        );
11252        multibuffer.push_excerpts(
11253            buffer_3.clone(),
11254            [
11255                ExcerptRange {
11256                    context: Point::new(0, 0)..Point::new(3, 0),
11257                    primary: None,
11258                },
11259                ExcerptRange {
11260                    context: Point::new(5, 0)..Point::new(7, 0),
11261                    primary: None,
11262                },
11263                ExcerptRange {
11264                    context: Point::new(9, 0)..Point::new(10, 4),
11265                    primary: None,
11266                },
11267            ],
11268            cx,
11269        );
11270        multibuffer
11271    });
11272
11273    let fs = FakeFs::new(cx.executor());
11274    fs.insert_tree(
11275        "/a",
11276        json!({
11277            "main.rs": sample_text_1,
11278            "other.rs": sample_text_2,
11279            "lib.rs": sample_text_3,
11280        }),
11281    )
11282    .await;
11283    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11284    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11285    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11286    let multi_buffer_editor = cx.new_view(|cx| {
11287        Editor::new(
11288            EditorMode::Full,
11289            multi_buffer,
11290            Some(project.clone()),
11291            true,
11292            cx,
11293        )
11294    });
11295    let multibuffer_item_id = workspace
11296        .update(cx, |workspace, cx| {
11297            assert!(
11298                workspace.active_item(cx).is_none(),
11299                "active item should be None before the first item is added"
11300            );
11301            workspace.add_item_to_active_pane(
11302                Box::new(multi_buffer_editor.clone()),
11303                None,
11304                true,
11305                cx,
11306            );
11307            let active_item = workspace
11308                .active_item(cx)
11309                .expect("should have an active item after adding the multi buffer");
11310            assert!(
11311                !active_item.is_singleton(cx),
11312                "A multi buffer was expected to active after adding"
11313            );
11314            active_item.item_id()
11315        })
11316        .unwrap();
11317    cx.executor().run_until_parked();
11318
11319    multi_buffer_editor.update(cx, |editor, cx| {
11320        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11321        editor.open_excerpts(&OpenExcerpts, cx);
11322    });
11323    cx.executor().run_until_parked();
11324    let first_item_id = workspace
11325        .update(cx, |workspace, cx| {
11326            let active_item = workspace
11327                .active_item(cx)
11328                .expect("should have an active item after navigating into the 1st buffer");
11329            let first_item_id = active_item.item_id();
11330            assert_ne!(
11331                first_item_id, multibuffer_item_id,
11332                "Should navigate into the 1st buffer and activate it"
11333            );
11334            assert!(
11335                active_item.is_singleton(cx),
11336                "New active item should be a singleton buffer"
11337            );
11338            assert_eq!(
11339                active_item
11340                    .act_as::<Editor>(cx)
11341                    .expect("should have navigated into an editor for the 1st buffer")
11342                    .read(cx)
11343                    .text(cx),
11344                sample_text_1
11345            );
11346
11347            workspace
11348                .go_back(workspace.active_pane().downgrade(), cx)
11349                .detach_and_log_err(cx);
11350
11351            first_item_id
11352        })
11353        .unwrap();
11354    cx.executor().run_until_parked();
11355    workspace
11356        .update(cx, |workspace, cx| {
11357            let active_item = workspace
11358                .active_item(cx)
11359                .expect("should have an active item after navigating back");
11360            assert_eq!(
11361                active_item.item_id(),
11362                multibuffer_item_id,
11363                "Should navigate back to the multi buffer"
11364            );
11365            assert!(!active_item.is_singleton(cx));
11366        })
11367        .unwrap();
11368
11369    multi_buffer_editor.update(cx, |editor, cx| {
11370        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11371            s.select_ranges(Some(39..40))
11372        });
11373        editor.open_excerpts(&OpenExcerpts, cx);
11374    });
11375    cx.executor().run_until_parked();
11376    let second_item_id = workspace
11377        .update(cx, |workspace, cx| {
11378            let active_item = workspace
11379                .active_item(cx)
11380                .expect("should have an active item after navigating into the 2nd buffer");
11381            let second_item_id = active_item.item_id();
11382            assert_ne!(
11383                second_item_id, multibuffer_item_id,
11384                "Should navigate away from the multibuffer"
11385            );
11386            assert_ne!(
11387                second_item_id, first_item_id,
11388                "Should navigate into the 2nd buffer and activate it"
11389            );
11390            assert!(
11391                active_item.is_singleton(cx),
11392                "New active item should be a singleton buffer"
11393            );
11394            assert_eq!(
11395                active_item
11396                    .act_as::<Editor>(cx)
11397                    .expect("should have navigated into an editor")
11398                    .read(cx)
11399                    .text(cx),
11400                sample_text_2
11401            );
11402
11403            workspace
11404                .go_back(workspace.active_pane().downgrade(), cx)
11405                .detach_and_log_err(cx);
11406
11407            second_item_id
11408        })
11409        .unwrap();
11410    cx.executor().run_until_parked();
11411    workspace
11412        .update(cx, |workspace, cx| {
11413            let active_item = workspace
11414                .active_item(cx)
11415                .expect("should have an active item after navigating back from the 2nd buffer");
11416            assert_eq!(
11417                active_item.item_id(),
11418                multibuffer_item_id,
11419                "Should navigate back from the 2nd buffer to the multi buffer"
11420            );
11421            assert!(!active_item.is_singleton(cx));
11422        })
11423        .unwrap();
11424
11425    multi_buffer_editor.update(cx, |editor, cx| {
11426        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11427            s.select_ranges(Some(60..70))
11428        });
11429        editor.open_excerpts(&OpenExcerpts, cx);
11430    });
11431    cx.executor().run_until_parked();
11432    workspace
11433        .update(cx, |workspace, cx| {
11434            let active_item = workspace
11435                .active_item(cx)
11436                .expect("should have an active item after navigating into the 3rd buffer");
11437            let third_item_id = active_item.item_id();
11438            assert_ne!(
11439                third_item_id, multibuffer_item_id,
11440                "Should navigate into the 3rd buffer and activate it"
11441            );
11442            assert_ne!(third_item_id, first_item_id);
11443            assert_ne!(third_item_id, second_item_id);
11444            assert!(
11445                active_item.is_singleton(cx),
11446                "New active item should be a singleton buffer"
11447            );
11448            assert_eq!(
11449                active_item
11450                    .act_as::<Editor>(cx)
11451                    .expect("should have navigated into an editor")
11452                    .read(cx)
11453                    .text(cx),
11454                sample_text_3
11455            );
11456
11457            workspace
11458                .go_back(workspace.active_pane().downgrade(), cx)
11459                .detach_and_log_err(cx);
11460        })
11461        .unwrap();
11462    cx.executor().run_until_parked();
11463    workspace
11464        .update(cx, |workspace, cx| {
11465            let active_item = workspace
11466                .active_item(cx)
11467                .expect("should have an active item after navigating back from the 3rd buffer");
11468            assert_eq!(
11469                active_item.item_id(),
11470                multibuffer_item_id,
11471                "Should navigate back from the 3rd buffer to the multi buffer"
11472            );
11473            assert!(!active_item.is_singleton(cx));
11474        })
11475        .unwrap();
11476}
11477
11478#[gpui::test]
11479async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11480    init_test(cx, |_| {});
11481
11482    let mut cx = EditorTestContext::new(cx).await;
11483
11484    let diff_base = r#"
11485        use some::mod;
11486
11487        const A: u32 = 42;
11488
11489        fn main() {
11490            println!("hello");
11491
11492            println!("world");
11493        }
11494        "#
11495    .unindent();
11496
11497    cx.set_state(
11498        &r#"
11499        use some::modified;
11500
11501        ˇ
11502        fn main() {
11503            println!("hello there");
11504
11505            println!("around the");
11506            println!("world");
11507        }
11508        "#
11509        .unindent(),
11510    );
11511
11512    cx.set_diff_base(Some(&diff_base));
11513    executor.run_until_parked();
11514
11515    cx.update_editor(|editor, cx| {
11516        editor.go_to_next_hunk(&GoToHunk, cx);
11517        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11518    });
11519    executor.run_until_parked();
11520    cx.assert_diff_hunks(
11521        r#"
11522          use some::modified;
11523
11524
11525          fn main() {
11526        -     println!("hello");
11527        +     println!("hello there");
11528
11529              println!("around the");
11530              println!("world");
11531          }
11532        "#
11533        .unindent(),
11534    );
11535
11536    cx.update_editor(|editor, cx| {
11537        for _ in 0..3 {
11538            editor.go_to_next_hunk(&GoToHunk, cx);
11539            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11540        }
11541    });
11542    executor.run_until_parked();
11543    cx.assert_editor_state(
11544        &r#"
11545        use some::modified;
11546
11547        ˇ
11548        fn main() {
11549            println!("hello there");
11550
11551            println!("around the");
11552            println!("world");
11553        }
11554        "#
11555        .unindent(),
11556    );
11557
11558    cx.assert_diff_hunks(
11559        r#"
11560        - use some::mod;
11561        + use some::modified;
11562
11563        - const A: u32 = 42;
11564
11565          fn main() {
11566        -     println!("hello");
11567        +     println!("hello there");
11568
11569        +     println!("around the");
11570              println!("world");
11571          }
11572        "#
11573        .unindent(),
11574    );
11575
11576    cx.update_editor(|editor, cx| {
11577        editor.cancel(&Cancel, cx);
11578    });
11579
11580    cx.assert_diff_hunks(
11581        r#"
11582          use some::modified;
11583
11584
11585          fn main() {
11586              println!("hello there");
11587
11588              println!("around the");
11589              println!("world");
11590          }
11591        "#
11592        .unindent(),
11593    );
11594}
11595
11596#[gpui::test]
11597async fn test_diff_base_change_with_expanded_diff_hunks(
11598    executor: BackgroundExecutor,
11599    cx: &mut gpui::TestAppContext,
11600) {
11601    init_test(cx, |_| {});
11602
11603    let mut cx = EditorTestContext::new(cx).await;
11604
11605    let diff_base = r#"
11606        use some::mod1;
11607        use some::mod2;
11608
11609        const A: u32 = 42;
11610        const B: u32 = 42;
11611        const C: u32 = 42;
11612
11613        fn main() {
11614            println!("hello");
11615
11616            println!("world");
11617        }
11618        "#
11619    .unindent();
11620
11621    cx.set_state(
11622        &r#"
11623        use some::mod2;
11624
11625        const A: u32 = 42;
11626        const C: u32 = 42;
11627
11628        fn main(ˇ) {
11629            //println!("hello");
11630
11631            println!("world");
11632            //
11633            //
11634        }
11635        "#
11636        .unindent(),
11637    );
11638
11639    cx.set_diff_base(Some(&diff_base));
11640    executor.run_until_parked();
11641
11642    cx.update_editor(|editor, cx| {
11643        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11644    });
11645    executor.run_until_parked();
11646    cx.assert_diff_hunks(
11647        r#"
11648        - use some::mod1;
11649          use some::mod2;
11650
11651          const A: u32 = 42;
11652        - const B: u32 = 42;
11653          const C: u32 = 42;
11654
11655          fn main() {
11656        -     println!("hello");
11657        +     //println!("hello");
11658
11659              println!("world");
11660        +     //
11661        +     //
11662          }
11663        "#
11664        .unindent(),
11665    );
11666
11667    cx.set_diff_base(Some("new diff base!"));
11668    executor.run_until_parked();
11669    cx.assert_diff_hunks(
11670        r#"
11671          use some::mod2;
11672
11673          const A: u32 = 42;
11674          const C: u32 = 42;
11675
11676          fn main() {
11677              //println!("hello");
11678
11679              println!("world");
11680              //
11681              //
11682          }
11683        "#
11684        .unindent(),
11685    );
11686
11687    cx.update_editor(|editor, cx| {
11688        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11689    });
11690    executor.run_until_parked();
11691    cx.assert_diff_hunks(
11692        r#"
11693        - new diff base!
11694        + use some::mod2;
11695        +
11696        + const A: u32 = 42;
11697        + const C: u32 = 42;
11698        +
11699        + fn main() {
11700        +     //println!("hello");
11701        +
11702        +     println!("world");
11703        +     //
11704        +     //
11705        + }
11706        "#
11707        .unindent(),
11708    );
11709}
11710
11711#[gpui::test]
11712async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11713    init_test(cx, |_| {});
11714
11715    let mut cx = EditorTestContext::new(cx).await;
11716
11717    let diff_base = r#"
11718        use some::mod1;
11719        use some::mod2;
11720
11721        const A: u32 = 42;
11722        const B: u32 = 42;
11723        const C: u32 = 42;
11724
11725        fn main() {
11726            println!("hello");
11727
11728            println!("world");
11729        }
11730
11731        fn another() {
11732            println!("another");
11733        }
11734
11735        fn another2() {
11736            println!("another2");
11737        }
11738        "#
11739    .unindent();
11740
11741    cx.set_state(
11742        &r#"
11743        «use some::mod2;
11744
11745        const A: u32 = 42;
11746        const C: u32 = 42;
11747
11748        fn main() {
11749            //println!("hello");
11750
11751            println!("world");
11752            //
11753            //ˇ»
11754        }
11755
11756        fn another() {
11757            println!("another");
11758            println!("another");
11759        }
11760
11761            println!("another2");
11762        }
11763        "#
11764        .unindent(),
11765    );
11766
11767    cx.set_diff_base(Some(&diff_base));
11768    executor.run_until_parked();
11769
11770    cx.update_editor(|editor, cx| {
11771        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11772    });
11773    executor.run_until_parked();
11774
11775    cx.assert_diff_hunks(
11776        r#"
11777        - use some::mod1;
11778          use some::mod2;
11779
11780          const A: u32 = 42;
11781        - const B: u32 = 42;
11782          const C: u32 = 42;
11783
11784          fn main() {
11785        -     println!("hello");
11786        +     //println!("hello");
11787
11788              println!("world");
11789        +     //
11790        +     //
11791          }
11792
11793          fn another() {
11794              println!("another");
11795        +     println!("another");
11796          }
11797
11798        - fn another2() {
11799              println!("another2");
11800          }
11801        "#
11802        .unindent(),
11803    );
11804
11805    // Fold across some of the diff hunks. They should no longer appear expanded.
11806    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11807    cx.executor().run_until_parked();
11808
11809    // Hunks are not shown if their position is within a fold
11810    cx.assert_diff_hunks(
11811        r#"
11812          use some::mod2;
11813
11814          const A: u32 = 42;
11815          const C: u32 = 42;
11816
11817          fn main() {
11818              //println!("hello");
11819
11820              println!("world");
11821              //
11822              //
11823          }
11824
11825          fn another() {
11826              println!("another");
11827        +     println!("another");
11828          }
11829
11830        - fn another2() {
11831              println!("another2");
11832          }
11833        "#
11834        .unindent(),
11835    );
11836
11837    cx.update_editor(|editor, cx| {
11838        editor.select_all(&SelectAll, cx);
11839        editor.unfold_lines(&UnfoldLines, cx);
11840    });
11841    cx.executor().run_until_parked();
11842
11843    // The deletions reappear when unfolding.
11844    cx.assert_diff_hunks(
11845        r#"
11846        - use some::mod1;
11847          use some::mod2;
11848
11849          const A: u32 = 42;
11850        - const B: u32 = 42;
11851          const C: u32 = 42;
11852
11853          fn main() {
11854        -     println!("hello");
11855        +     //println!("hello");
11856
11857              println!("world");
11858        +     //
11859        +     //
11860          }
11861
11862          fn another() {
11863              println!("another");
11864        +     println!("another");
11865          }
11866
11867        - fn another2() {
11868              println!("another2");
11869          }
11870        "#
11871        .unindent(),
11872    );
11873}
11874
11875#[gpui::test]
11876async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11877    init_test(cx, |_| {});
11878
11879    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11880    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11881    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11882    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11883    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11884    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11885
11886    let buffer_1 = cx.new_model(|cx| {
11887        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11888        buffer.set_diff_base(Some(file_1_old.into()), cx);
11889        buffer
11890    });
11891    let buffer_2 = cx.new_model(|cx| {
11892        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11893        buffer.set_diff_base(Some(file_2_old.into()), cx);
11894        buffer
11895    });
11896    let buffer_3 = cx.new_model(|cx| {
11897        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11898        buffer.set_diff_base(Some(file_3_old.into()), cx);
11899        buffer
11900    });
11901
11902    let multi_buffer = cx.new_model(|cx| {
11903        let mut multibuffer = MultiBuffer::new(ReadWrite);
11904        multibuffer.push_excerpts(
11905            buffer_1.clone(),
11906            [
11907                ExcerptRange {
11908                    context: Point::new(0, 0)..Point::new(3, 0),
11909                    primary: None,
11910                },
11911                ExcerptRange {
11912                    context: Point::new(5, 0)..Point::new(7, 0),
11913                    primary: None,
11914                },
11915                ExcerptRange {
11916                    context: Point::new(9, 0)..Point::new(10, 3),
11917                    primary: None,
11918                },
11919            ],
11920            cx,
11921        );
11922        multibuffer.push_excerpts(
11923            buffer_2.clone(),
11924            [
11925                ExcerptRange {
11926                    context: Point::new(0, 0)..Point::new(3, 0),
11927                    primary: None,
11928                },
11929                ExcerptRange {
11930                    context: Point::new(5, 0)..Point::new(7, 0),
11931                    primary: None,
11932                },
11933                ExcerptRange {
11934                    context: Point::new(9, 0)..Point::new(10, 3),
11935                    primary: None,
11936                },
11937            ],
11938            cx,
11939        );
11940        multibuffer.push_excerpts(
11941            buffer_3.clone(),
11942            [
11943                ExcerptRange {
11944                    context: Point::new(0, 0)..Point::new(3, 0),
11945                    primary: None,
11946                },
11947                ExcerptRange {
11948                    context: Point::new(5, 0)..Point::new(7, 0),
11949                    primary: None,
11950                },
11951                ExcerptRange {
11952                    context: Point::new(9, 0)..Point::new(10, 3),
11953                    primary: None,
11954                },
11955            ],
11956            cx,
11957        );
11958        multibuffer
11959    });
11960
11961    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11962    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11963    cx.run_until_parked();
11964
11965    cx.assert_editor_state(
11966        &"
11967            ˇaaa
11968            ccc
11969            ddd
11970
11971            ggg
11972            hhh
11973
11974
11975            lll
11976            mmm
11977            NNN
11978
11979            qqq
11980            rrr
11981
11982            uuu
11983            111
11984            222
11985            333
11986
11987            666
11988            777
11989
11990            000
11991            !!!"
11992        .unindent(),
11993    );
11994
11995    cx.update_editor(|editor, cx| {
11996        editor.select_all(&SelectAll, cx);
11997        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11998    });
11999    cx.executor().run_until_parked();
12000
12001    cx.assert_diff_hunks(
12002        "
12003            aaa
12004          - bbb
12005            ccc
12006            ddd
12007
12008            ggg
12009            hhh
12010
12011
12012            lll
12013            mmm
12014          - nnn
12015          + NNN
12016
12017            qqq
12018            rrr
12019
12020            uuu
12021            111
12022            222
12023            333
12024
12025          + 666
12026            777
12027
12028            000
12029            !!!"
12030        .unindent(),
12031    );
12032}
12033
12034#[gpui::test]
12035async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12036    init_test(cx, |_| {});
12037
12038    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12039    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12040
12041    let buffer = cx.new_model(|cx| {
12042        let mut buffer = Buffer::local(text.to_string(), cx);
12043        buffer.set_diff_base(Some(base.into()), cx);
12044        buffer
12045    });
12046
12047    let multi_buffer = cx.new_model(|cx| {
12048        let mut multibuffer = MultiBuffer::new(ReadWrite);
12049        multibuffer.push_excerpts(
12050            buffer.clone(),
12051            [
12052                ExcerptRange {
12053                    context: Point::new(0, 0)..Point::new(2, 0),
12054                    primary: None,
12055                },
12056                ExcerptRange {
12057                    context: Point::new(5, 0)..Point::new(7, 0),
12058                    primary: None,
12059                },
12060            ],
12061            cx,
12062        );
12063        multibuffer
12064    });
12065
12066    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12067    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12068    cx.run_until_parked();
12069
12070    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12071    cx.executor().run_until_parked();
12072
12073    cx.assert_diff_hunks(
12074        "
12075            aaa
12076          - bbb
12077          + BBB
12078
12079          - ddd
12080          - eee
12081          + EEE
12082            fff
12083        "
12084        .unindent(),
12085    );
12086}
12087
12088#[gpui::test]
12089async fn test_edits_around_expanded_insertion_hunks(
12090    executor: BackgroundExecutor,
12091    cx: &mut gpui::TestAppContext,
12092) {
12093    init_test(cx, |_| {});
12094
12095    let mut cx = EditorTestContext::new(cx).await;
12096
12097    let diff_base = r#"
12098        use some::mod1;
12099        use some::mod2;
12100
12101        const A: u32 = 42;
12102
12103        fn main() {
12104            println!("hello");
12105
12106            println!("world");
12107        }
12108        "#
12109    .unindent();
12110    executor.run_until_parked();
12111    cx.set_state(
12112        &r#"
12113        use some::mod1;
12114        use some::mod2;
12115
12116        const A: u32 = 42;
12117        const B: u32 = 42;
12118        const C: u32 = 42;
12119        ˇ
12120
12121        fn main() {
12122            println!("hello");
12123
12124            println!("world");
12125        }
12126        "#
12127        .unindent(),
12128    );
12129
12130    cx.set_diff_base(Some(&diff_base));
12131    executor.run_until_parked();
12132
12133    cx.update_editor(|editor, cx| {
12134        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12135    });
12136    executor.run_until_parked();
12137
12138    cx.assert_diff_hunks(
12139        r#"
12140        use some::mod1;
12141        use some::mod2;
12142
12143        const A: u32 = 42;
12144      + const B: u32 = 42;
12145      + const C: u32 = 42;
12146      +
12147
12148        fn main() {
12149            println!("hello");
12150
12151            println!("world");
12152        }
12153        "#
12154        .unindent(),
12155    );
12156
12157    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12158    executor.run_until_parked();
12159
12160    cx.assert_diff_hunks(
12161        r#"
12162        use some::mod1;
12163        use some::mod2;
12164
12165        const A: u32 = 42;
12166      + const B: u32 = 42;
12167      + const C: u32 = 42;
12168      + const D: u32 = 42;
12169      +
12170
12171        fn main() {
12172            println!("hello");
12173
12174            println!("world");
12175        }
12176        "#
12177        .unindent(),
12178    );
12179
12180    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12181    executor.run_until_parked();
12182
12183    cx.assert_diff_hunks(
12184        r#"
12185        use some::mod1;
12186        use some::mod2;
12187
12188        const A: u32 = 42;
12189      + const B: u32 = 42;
12190      + const C: u32 = 42;
12191      + const D: u32 = 42;
12192      + const E: u32 = 42;
12193      +
12194
12195        fn main() {
12196            println!("hello");
12197
12198            println!("world");
12199        }
12200        "#
12201        .unindent(),
12202    );
12203
12204    cx.update_editor(|editor, cx| {
12205        editor.delete_line(&DeleteLine, cx);
12206    });
12207    executor.run_until_parked();
12208
12209    cx.assert_diff_hunks(
12210        r#"
12211        use some::mod1;
12212        use some::mod2;
12213
12214        const A: u32 = 42;
12215      + const B: u32 = 42;
12216      + const C: u32 = 42;
12217      + const D: u32 = 42;
12218      + const E: u32 = 42;
12219
12220        fn main() {
12221            println!("hello");
12222
12223            println!("world");
12224        }
12225        "#
12226        .unindent(),
12227    );
12228
12229    cx.update_editor(|editor, cx| {
12230        editor.move_up(&MoveUp, cx);
12231        editor.delete_line(&DeleteLine, cx);
12232        editor.move_up(&MoveUp, cx);
12233        editor.delete_line(&DeleteLine, cx);
12234        editor.move_up(&MoveUp, cx);
12235        editor.delete_line(&DeleteLine, cx);
12236    });
12237    executor.run_until_parked();
12238    cx.assert_editor_state(
12239        &r#"
12240        use some::mod1;
12241        use some::mod2;
12242
12243        const A: u32 = 42;
12244        const B: u32 = 42;
12245        ˇ
12246        fn main() {
12247            println!("hello");
12248
12249            println!("world");
12250        }
12251        "#
12252        .unindent(),
12253    );
12254
12255    cx.assert_diff_hunks(
12256        r#"
12257        use some::mod1;
12258        use some::mod2;
12259
12260        const A: u32 = 42;
12261      + const B: u32 = 42;
12262
12263        fn main() {
12264            println!("hello");
12265
12266            println!("world");
12267        }
12268        "#
12269        .unindent(),
12270    );
12271
12272    cx.update_editor(|editor, cx| {
12273        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12274        editor.delete_line(&DeleteLine, cx);
12275    });
12276    executor.run_until_parked();
12277    cx.assert_diff_hunks(
12278        r#"
12279        use some::mod1;
12280      - use some::mod2;
12281      -
12282      - const A: u32 = 42;
12283
12284        fn main() {
12285            println!("hello");
12286
12287            println!("world");
12288        }
12289        "#
12290        .unindent(),
12291    );
12292}
12293
12294#[gpui::test]
12295async fn test_edits_around_expanded_deletion_hunks(
12296    executor: BackgroundExecutor,
12297    cx: &mut gpui::TestAppContext,
12298) {
12299    init_test(cx, |_| {});
12300
12301    let mut cx = EditorTestContext::new(cx).await;
12302
12303    let diff_base = r#"
12304        use some::mod1;
12305        use some::mod2;
12306
12307        const A: u32 = 42;
12308        const B: u32 = 42;
12309        const C: u32 = 42;
12310
12311
12312        fn main() {
12313            println!("hello");
12314
12315            println!("world");
12316        }
12317    "#
12318    .unindent();
12319    executor.run_until_parked();
12320    cx.set_state(
12321        &r#"
12322        use some::mod1;
12323        use some::mod2;
12324
12325        ˇconst B: u32 = 42;
12326        const C: u32 = 42;
12327
12328
12329        fn main() {
12330            println!("hello");
12331
12332            println!("world");
12333        }
12334        "#
12335        .unindent(),
12336    );
12337
12338    cx.set_diff_base(Some(&diff_base));
12339    executor.run_until_parked();
12340
12341    cx.update_editor(|editor, cx| {
12342        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12343    });
12344    executor.run_until_parked();
12345
12346    cx.assert_diff_hunks(
12347        r#"
12348        use some::mod1;
12349        use some::mod2;
12350
12351      - const A: u32 = 42;
12352        const B: u32 = 42;
12353        const C: u32 = 42;
12354
12355
12356        fn main() {
12357            println!("hello");
12358
12359            println!("world");
12360        }
12361        "#
12362        .unindent(),
12363    );
12364
12365    cx.update_editor(|editor, cx| {
12366        editor.delete_line(&DeleteLine, cx);
12367    });
12368    executor.run_until_parked();
12369    cx.assert_editor_state(
12370        &r#"
12371        use some::mod1;
12372        use some::mod2;
12373
12374        ˇconst C: u32 = 42;
12375
12376
12377        fn main() {
12378            println!("hello");
12379
12380            println!("world");
12381        }
12382        "#
12383        .unindent(),
12384    );
12385    cx.assert_diff_hunks(
12386        r#"
12387        use some::mod1;
12388        use some::mod2;
12389
12390      - const A: u32 = 42;
12391      - const B: u32 = 42;
12392        const C: u32 = 42;
12393
12394
12395        fn main() {
12396            println!("hello");
12397
12398            println!("world");
12399        }
12400        "#
12401        .unindent(),
12402    );
12403
12404    cx.update_editor(|editor, cx| {
12405        editor.delete_line(&DeleteLine, cx);
12406    });
12407    executor.run_until_parked();
12408    cx.assert_editor_state(
12409        &r#"
12410        use some::mod1;
12411        use some::mod2;
12412
12413        ˇ
12414
12415        fn main() {
12416            println!("hello");
12417
12418            println!("world");
12419        }
12420        "#
12421        .unindent(),
12422    );
12423    cx.assert_diff_hunks(
12424        r#"
12425        use some::mod1;
12426        use some::mod2;
12427
12428      - const A: u32 = 42;
12429      - const B: u32 = 42;
12430      - const C: u32 = 42;
12431
12432
12433        fn main() {
12434            println!("hello");
12435
12436            println!("world");
12437        }
12438        "#
12439        .unindent(),
12440    );
12441
12442    cx.update_editor(|editor, cx| {
12443        editor.handle_input("replacement", cx);
12444    });
12445    executor.run_until_parked();
12446    cx.assert_editor_state(
12447        &r#"
12448        use some::mod1;
12449        use some::mod2;
12450
12451        replacementˇ
12452
12453        fn main() {
12454            println!("hello");
12455
12456            println!("world");
12457        }
12458        "#
12459        .unindent(),
12460    );
12461    cx.assert_diff_hunks(
12462        r#"
12463        use some::mod1;
12464        use some::mod2;
12465
12466      - const A: u32 = 42;
12467      - const B: u32 = 42;
12468      - const C: u32 = 42;
12469      -
12470      + replacement
12471
12472        fn main() {
12473            println!("hello");
12474
12475            println!("world");
12476        }
12477        "#
12478        .unindent(),
12479    );
12480}
12481
12482#[gpui::test]
12483async fn test_edit_after_expanded_modification_hunk(
12484    executor: BackgroundExecutor,
12485    cx: &mut gpui::TestAppContext,
12486) {
12487    init_test(cx, |_| {});
12488
12489    let mut cx = EditorTestContext::new(cx).await;
12490
12491    let diff_base = r#"
12492        use some::mod1;
12493        use some::mod2;
12494
12495        const A: u32 = 42;
12496        const B: u32 = 42;
12497        const C: u32 = 42;
12498        const D: u32 = 42;
12499
12500
12501        fn main() {
12502            println!("hello");
12503
12504            println!("world");
12505        }"#
12506    .unindent();
12507
12508    cx.set_state(
12509        &r#"
12510        use some::mod1;
12511        use some::mod2;
12512
12513        const A: u32 = 42;
12514        const B: u32 = 42;
12515        const C: u32 = 43ˇ
12516        const D: u32 = 42;
12517
12518
12519        fn main() {
12520            println!("hello");
12521
12522            println!("world");
12523        }"#
12524        .unindent(),
12525    );
12526
12527    cx.set_diff_base(Some(&diff_base));
12528    executor.run_until_parked();
12529    cx.update_editor(|editor, cx| {
12530        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12531    });
12532    executor.run_until_parked();
12533
12534    cx.assert_diff_hunks(
12535        r#"
12536        use some::mod1;
12537        use some::mod2;
12538
12539        const A: u32 = 42;
12540        const B: u32 = 42;
12541      - const C: u32 = 42;
12542      + const C: u32 = 43
12543        const D: u32 = 42;
12544
12545
12546        fn main() {
12547            println!("hello");
12548
12549            println!("world");
12550        }"#
12551        .unindent(),
12552    );
12553
12554    cx.update_editor(|editor, cx| {
12555        editor.handle_input("\nnew_line\n", cx);
12556    });
12557    executor.run_until_parked();
12558
12559    cx.assert_diff_hunks(
12560        r#"
12561        use some::mod1;
12562        use some::mod2;
12563
12564        const A: u32 = 42;
12565        const B: u32 = 42;
12566      - const C: u32 = 42;
12567      + const C: u32 = 43
12568      + new_line
12569      +
12570        const D: u32 = 42;
12571
12572
12573        fn main() {
12574            println!("hello");
12575
12576            println!("world");
12577        }"#
12578        .unindent(),
12579    );
12580}
12581
12582async fn setup_indent_guides_editor(
12583    text: &str,
12584    cx: &mut gpui::TestAppContext,
12585) -> (BufferId, EditorTestContext) {
12586    init_test(cx, |_| {});
12587
12588    let mut cx = EditorTestContext::new(cx).await;
12589
12590    let buffer_id = cx.update_editor(|editor, cx| {
12591        editor.set_text(text, cx);
12592        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12593
12594        buffer_ids[0]
12595    });
12596
12597    (buffer_id, cx)
12598}
12599
12600fn assert_indent_guides(
12601    range: Range<u32>,
12602    expected: Vec<IndentGuide>,
12603    active_indices: Option<Vec<usize>>,
12604    cx: &mut EditorTestContext,
12605) {
12606    let indent_guides = cx.update_editor(|editor, cx| {
12607        let snapshot = editor.snapshot(cx).display_snapshot;
12608        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12609            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12610            true,
12611            &snapshot,
12612            cx,
12613        );
12614
12615        indent_guides.sort_by(|a, b| {
12616            a.depth.cmp(&b.depth).then(
12617                a.start_row
12618                    .cmp(&b.start_row)
12619                    .then(a.end_row.cmp(&b.end_row)),
12620            )
12621        });
12622        indent_guides
12623    });
12624
12625    if let Some(expected) = active_indices {
12626        let active_indices = cx.update_editor(|editor, cx| {
12627            let snapshot = editor.snapshot(cx).display_snapshot;
12628            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12629        });
12630
12631        assert_eq!(
12632            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12633            expected,
12634            "Active indent guide indices do not match"
12635        );
12636    }
12637
12638    let expected: Vec<_> = expected
12639        .into_iter()
12640        .map(|guide| MultiBufferIndentGuide {
12641            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12642            buffer: guide,
12643        })
12644        .collect();
12645
12646    assert_eq!(indent_guides, expected, "Indent guides do not match");
12647}
12648
12649fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12650    IndentGuide {
12651        buffer_id,
12652        start_row,
12653        end_row,
12654        depth,
12655        tab_size: 4,
12656        settings: IndentGuideSettings {
12657            enabled: true,
12658            line_width: 1,
12659            active_line_width: 1,
12660            ..Default::default()
12661        },
12662    }
12663}
12664
12665#[gpui::test]
12666async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12667    let (buffer_id, mut cx) = setup_indent_guides_editor(
12668        &"
12669    fn main() {
12670        let a = 1;
12671    }"
12672        .unindent(),
12673        cx,
12674    )
12675    .await;
12676
12677    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12678}
12679
12680#[gpui::test]
12681async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12682    let (buffer_id, mut cx) = setup_indent_guides_editor(
12683        &"
12684    fn main() {
12685        let a = 1;
12686        let b = 2;
12687    }"
12688        .unindent(),
12689        cx,
12690    )
12691    .await;
12692
12693    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12694}
12695
12696#[gpui::test]
12697async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12698    let (buffer_id, mut cx) = setup_indent_guides_editor(
12699        &"
12700    fn main() {
12701        let a = 1;
12702        if a == 3 {
12703            let b = 2;
12704        } else {
12705            let c = 3;
12706        }
12707    }"
12708        .unindent(),
12709        cx,
12710    )
12711    .await;
12712
12713    assert_indent_guides(
12714        0..8,
12715        vec![
12716            indent_guide(buffer_id, 1, 6, 0),
12717            indent_guide(buffer_id, 3, 3, 1),
12718            indent_guide(buffer_id, 5, 5, 1),
12719        ],
12720        None,
12721        &mut cx,
12722    );
12723}
12724
12725#[gpui::test]
12726async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12727    let (buffer_id, mut cx) = setup_indent_guides_editor(
12728        &"
12729    fn main() {
12730        let a = 1;
12731            let b = 2;
12732        let c = 3;
12733    }"
12734        .unindent(),
12735        cx,
12736    )
12737    .await;
12738
12739    assert_indent_guides(
12740        0..5,
12741        vec![
12742            indent_guide(buffer_id, 1, 3, 0),
12743            indent_guide(buffer_id, 2, 2, 1),
12744        ],
12745        None,
12746        &mut cx,
12747    );
12748}
12749
12750#[gpui::test]
12751async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12752    let (buffer_id, mut cx) = setup_indent_guides_editor(
12753        &"
12754        fn main() {
12755            let a = 1;
12756
12757            let c = 3;
12758        }"
12759        .unindent(),
12760        cx,
12761    )
12762    .await;
12763
12764    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12765}
12766
12767#[gpui::test]
12768async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12769    let (buffer_id, mut cx) = setup_indent_guides_editor(
12770        &"
12771        fn main() {
12772            let a = 1;
12773
12774            let c = 3;
12775
12776            if a == 3 {
12777                let b = 2;
12778            } else {
12779                let c = 3;
12780            }
12781        }"
12782        .unindent(),
12783        cx,
12784    )
12785    .await;
12786
12787    assert_indent_guides(
12788        0..11,
12789        vec![
12790            indent_guide(buffer_id, 1, 9, 0),
12791            indent_guide(buffer_id, 6, 6, 1),
12792            indent_guide(buffer_id, 8, 8, 1),
12793        ],
12794        None,
12795        &mut cx,
12796    );
12797}
12798
12799#[gpui::test]
12800async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12801    let (buffer_id, mut cx) = setup_indent_guides_editor(
12802        &"
12803        fn main() {
12804            let a = 1;
12805
12806            let c = 3;
12807
12808            if a == 3 {
12809                let b = 2;
12810            } else {
12811                let c = 3;
12812            }
12813        }"
12814        .unindent(),
12815        cx,
12816    )
12817    .await;
12818
12819    assert_indent_guides(
12820        1..11,
12821        vec![
12822            indent_guide(buffer_id, 1, 9, 0),
12823            indent_guide(buffer_id, 6, 6, 1),
12824            indent_guide(buffer_id, 8, 8, 1),
12825        ],
12826        None,
12827        &mut cx,
12828    );
12829}
12830
12831#[gpui::test]
12832async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12833    let (buffer_id, mut cx) = setup_indent_guides_editor(
12834        &"
12835        fn main() {
12836            let a = 1;
12837
12838            let c = 3;
12839
12840            if a == 3 {
12841                let b = 2;
12842            } else {
12843                let c = 3;
12844            }
12845        }"
12846        .unindent(),
12847        cx,
12848    )
12849    .await;
12850
12851    assert_indent_guides(
12852        1..10,
12853        vec![
12854            indent_guide(buffer_id, 1, 9, 0),
12855            indent_guide(buffer_id, 6, 6, 1),
12856            indent_guide(buffer_id, 8, 8, 1),
12857        ],
12858        None,
12859        &mut cx,
12860    );
12861}
12862
12863#[gpui::test]
12864async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12865    let (buffer_id, mut cx) = setup_indent_guides_editor(
12866        &"
12867        block1
12868            block2
12869                block3
12870                    block4
12871            block2
12872        block1
12873        block1"
12874            .unindent(),
12875        cx,
12876    )
12877    .await;
12878
12879    assert_indent_guides(
12880        1..10,
12881        vec![
12882            indent_guide(buffer_id, 1, 4, 0),
12883            indent_guide(buffer_id, 2, 3, 1),
12884            indent_guide(buffer_id, 3, 3, 2),
12885        ],
12886        None,
12887        &mut cx,
12888    );
12889}
12890
12891#[gpui::test]
12892async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12893    let (buffer_id, mut cx) = setup_indent_guides_editor(
12894        &"
12895        block1
12896            block2
12897                block3
12898
12899        block1
12900        block1"
12901            .unindent(),
12902        cx,
12903    )
12904    .await;
12905
12906    assert_indent_guides(
12907        0..6,
12908        vec![
12909            indent_guide(buffer_id, 1, 2, 0),
12910            indent_guide(buffer_id, 2, 2, 1),
12911        ],
12912        None,
12913        &mut cx,
12914    );
12915}
12916
12917#[gpui::test]
12918async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12919    let (buffer_id, mut cx) = setup_indent_guides_editor(
12920        &"
12921        block1
12922
12923
12924
12925            block2
12926        "
12927        .unindent(),
12928        cx,
12929    )
12930    .await;
12931
12932    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12933}
12934
12935#[gpui::test]
12936async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12937    let (buffer_id, mut cx) = setup_indent_guides_editor(
12938        &"
12939        def a:
12940        \tb = 3
12941        \tif True:
12942        \t\tc = 4
12943        \t\td = 5
12944        \tprint(b)
12945        "
12946        .unindent(),
12947        cx,
12948    )
12949    .await;
12950
12951    assert_indent_guides(
12952        0..6,
12953        vec![
12954            indent_guide(buffer_id, 1, 6, 0),
12955            indent_guide(buffer_id, 3, 4, 1),
12956        ],
12957        None,
12958        &mut cx,
12959    );
12960}
12961
12962#[gpui::test]
12963async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12964    let (buffer_id, mut cx) = setup_indent_guides_editor(
12965        &"
12966    fn main() {
12967        let a = 1;
12968    }"
12969        .unindent(),
12970        cx,
12971    )
12972    .await;
12973
12974    cx.update_editor(|editor, cx| {
12975        editor.change_selections(None, cx, |s| {
12976            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12977        });
12978    });
12979
12980    assert_indent_guides(
12981        0..3,
12982        vec![indent_guide(buffer_id, 1, 1, 0)],
12983        Some(vec![0]),
12984        &mut cx,
12985    );
12986}
12987
12988#[gpui::test]
12989async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12990    let (buffer_id, mut cx) = setup_indent_guides_editor(
12991        &"
12992    fn main() {
12993        if 1 == 2 {
12994            let a = 1;
12995        }
12996    }"
12997        .unindent(),
12998        cx,
12999    )
13000    .await;
13001
13002    cx.update_editor(|editor, cx| {
13003        editor.change_selections(None, cx, |s| {
13004            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13005        });
13006    });
13007
13008    assert_indent_guides(
13009        0..4,
13010        vec![
13011            indent_guide(buffer_id, 1, 3, 0),
13012            indent_guide(buffer_id, 2, 2, 1),
13013        ],
13014        Some(vec![1]),
13015        &mut cx,
13016    );
13017
13018    cx.update_editor(|editor, cx| {
13019        editor.change_selections(None, cx, |s| {
13020            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13021        });
13022    });
13023
13024    assert_indent_guides(
13025        0..4,
13026        vec![
13027            indent_guide(buffer_id, 1, 3, 0),
13028            indent_guide(buffer_id, 2, 2, 1),
13029        ],
13030        Some(vec![1]),
13031        &mut cx,
13032    );
13033
13034    cx.update_editor(|editor, cx| {
13035        editor.change_selections(None, cx, |s| {
13036            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13037        });
13038    });
13039
13040    assert_indent_guides(
13041        0..4,
13042        vec![
13043            indent_guide(buffer_id, 1, 3, 0),
13044            indent_guide(buffer_id, 2, 2, 1),
13045        ],
13046        Some(vec![0]),
13047        &mut cx,
13048    );
13049}
13050
13051#[gpui::test]
13052async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13053    let (buffer_id, mut cx) = setup_indent_guides_editor(
13054        &"
13055    fn main() {
13056        let a = 1;
13057
13058        let b = 2;
13059    }"
13060        .unindent(),
13061        cx,
13062    )
13063    .await;
13064
13065    cx.update_editor(|editor, cx| {
13066        editor.change_selections(None, cx, |s| {
13067            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13068        });
13069    });
13070
13071    assert_indent_guides(
13072        0..5,
13073        vec![indent_guide(buffer_id, 1, 3, 0)],
13074        Some(vec![0]),
13075        &mut cx,
13076    );
13077}
13078
13079#[gpui::test]
13080async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13081    let (buffer_id, mut cx) = setup_indent_guides_editor(
13082        &"
13083    def m:
13084        a = 1
13085        pass"
13086            .unindent(),
13087        cx,
13088    )
13089    .await;
13090
13091    cx.update_editor(|editor, cx| {
13092        editor.change_selections(None, cx, |s| {
13093            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13094        });
13095    });
13096
13097    assert_indent_guides(
13098        0..3,
13099        vec![indent_guide(buffer_id, 1, 2, 0)],
13100        Some(vec![0]),
13101        &mut cx,
13102    );
13103}
13104
13105#[gpui::test]
13106fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13107    init_test(cx, |_| {});
13108
13109    let editor = cx.add_window(|cx| {
13110        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13111        build_editor(buffer, cx)
13112    });
13113
13114    let render_args = Arc::new(Mutex::new(None));
13115    let snapshot = editor
13116        .update(cx, |editor, cx| {
13117            let snapshot = editor.buffer().read(cx).snapshot(cx);
13118            let range =
13119                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13120
13121            struct RenderArgs {
13122                row: MultiBufferRow,
13123                folded: bool,
13124                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13125            }
13126
13127            let crease = Crease::new(
13128                range,
13129                FoldPlaceholder::test(),
13130                {
13131                    let toggle_callback = render_args.clone();
13132                    move |row, folded, callback, _cx| {
13133                        *toggle_callback.lock() = Some(RenderArgs {
13134                            row,
13135                            folded,
13136                            callback,
13137                        });
13138                        div()
13139                    }
13140                },
13141                |_row, _folded, _cx| div(),
13142            );
13143
13144            editor.insert_creases(Some(crease), cx);
13145            let snapshot = editor.snapshot(cx);
13146            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13147            snapshot
13148        })
13149        .unwrap();
13150
13151    let render_args = render_args.lock().take().unwrap();
13152    assert_eq!(render_args.row, MultiBufferRow(1));
13153    assert!(!render_args.folded);
13154    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13155
13156    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13157        .unwrap();
13158    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13159    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13160
13161    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13162        .unwrap();
13163    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13164    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13165}
13166
13167#[gpui::test]
13168async fn test_input_text(cx: &mut gpui::TestAppContext) {
13169    init_test(cx, |_| {});
13170    let mut cx = EditorTestContext::new(cx).await;
13171
13172    cx.set_state(
13173        &r#"ˇone
13174        two
13175
13176        three
13177        fourˇ
13178        five
13179
13180        siˇx"#
13181            .unindent(),
13182    );
13183
13184    cx.dispatch_action(HandleInput(String::new()));
13185    cx.assert_editor_state(
13186        &r#"ˇone
13187        two
13188
13189        three
13190        fourˇ
13191        five
13192
13193        siˇx"#
13194            .unindent(),
13195    );
13196
13197    cx.dispatch_action(HandleInput("AAAA".to_string()));
13198    cx.assert_editor_state(
13199        &r#"AAAAˇone
13200        two
13201
13202        three
13203        fourAAAAˇ
13204        five
13205
13206        siAAAAˇx"#
13207            .unindent(),
13208    );
13209}
13210
13211#[gpui::test]
13212async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13213    init_test(cx, |_| {});
13214
13215    let mut cx = EditorTestContext::new(cx).await;
13216    cx.set_state(
13217        r#"let foo = 1;
13218let foo = 2;
13219let foo = 3;
13220let fooˇ = 4;
13221let foo = 5;
13222let foo = 6;
13223let foo = 7;
13224let foo = 8;
13225let foo = 9;
13226let foo = 10;
13227let foo = 11;
13228let foo = 12;
13229let foo = 13;
13230let foo = 14;
13231let foo = 15;"#,
13232    );
13233
13234    cx.update_editor(|e, cx| {
13235        assert_eq!(
13236            e.next_scroll_position,
13237            NextScrollCursorCenterTopBottom::Center,
13238            "Default next scroll direction is center",
13239        );
13240
13241        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13242        assert_eq!(
13243            e.next_scroll_position,
13244            NextScrollCursorCenterTopBottom::Top,
13245            "After center, next scroll direction should be top",
13246        );
13247
13248        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13249        assert_eq!(
13250            e.next_scroll_position,
13251            NextScrollCursorCenterTopBottom::Bottom,
13252            "After top, next scroll direction should be bottom",
13253        );
13254
13255        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13256        assert_eq!(
13257            e.next_scroll_position,
13258            NextScrollCursorCenterTopBottom::Center,
13259            "After bottom, scrolling should start over",
13260        );
13261
13262        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13263        assert_eq!(
13264            e.next_scroll_position,
13265            NextScrollCursorCenterTopBottom::Top,
13266            "Scrolling continues if retriggered fast enough"
13267        );
13268    });
13269
13270    cx.executor()
13271        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13272    cx.executor().run_until_parked();
13273    cx.update_editor(|e, _| {
13274        assert_eq!(
13275            e.next_scroll_position,
13276            NextScrollCursorCenterTopBottom::Center,
13277            "If scrolling is not triggered fast enough, it should reset"
13278        );
13279    });
13280}
13281
13282#[gpui::test]
13283async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13284    init_test(cx, |_| {});
13285    let mut cx = EditorLspTestContext::new_rust(
13286        lsp::ServerCapabilities {
13287            definition_provider: Some(lsp::OneOf::Left(true)),
13288            references_provider: Some(lsp::OneOf::Left(true)),
13289            ..lsp::ServerCapabilities::default()
13290        },
13291        cx,
13292    )
13293    .await;
13294
13295    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13296        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13297            move |params, _| async move {
13298                if empty_go_to_definition {
13299                    Ok(None)
13300                } else {
13301                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13302                        uri: params.text_document_position_params.text_document.uri,
13303                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13304                    })))
13305                }
13306            },
13307        );
13308        let references =
13309            cx.lsp
13310                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13311                    Ok(Some(vec![lsp::Location {
13312                        uri: params.text_document_position.text_document.uri,
13313                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13314                    }]))
13315                });
13316        (go_to_definition, references)
13317    };
13318
13319    cx.set_state(
13320        &r#"fn one() {
13321            let mut a = ˇtwo();
13322        }
13323
13324        fn two() {}"#
13325            .unindent(),
13326    );
13327    set_up_lsp_handlers(false, &mut cx);
13328    let navigated = cx
13329        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13330        .await
13331        .expect("Failed to navigate to definition");
13332    assert_eq!(
13333        navigated,
13334        Navigated::Yes,
13335        "Should have navigated to definition from the GetDefinition response"
13336    );
13337    cx.assert_editor_state(
13338        &r#"fn one() {
13339            let mut a = two();
13340        }
13341
13342        fn «twoˇ»() {}"#
13343            .unindent(),
13344    );
13345
13346    let editors = cx.update_workspace(|workspace, cx| {
13347        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13348    });
13349    cx.update_editor(|_, test_editor_cx| {
13350        assert_eq!(
13351            editors.len(),
13352            1,
13353            "Initially, only one, test, editor should be open in the workspace"
13354        );
13355        assert_eq!(
13356            test_editor_cx.view(),
13357            editors.last().expect("Asserted len is 1")
13358        );
13359    });
13360
13361    set_up_lsp_handlers(true, &mut cx);
13362    let navigated = cx
13363        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13364        .await
13365        .expect("Failed to navigate to lookup references");
13366    assert_eq!(
13367        navigated,
13368        Navigated::Yes,
13369        "Should have navigated to references as a fallback after empty GoToDefinition response"
13370    );
13371    // We should not change the selections in the existing file,
13372    // if opening another milti buffer with the references
13373    cx.assert_editor_state(
13374        &r#"fn one() {
13375            let mut a = two();
13376        }
13377
13378        fn «twoˇ»() {}"#
13379            .unindent(),
13380    );
13381    let editors = cx.update_workspace(|workspace, cx| {
13382        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13383    });
13384    cx.update_editor(|_, test_editor_cx| {
13385        assert_eq!(
13386            editors.len(),
13387            2,
13388            "After falling back to references search, we open a new editor with the results"
13389        );
13390        let references_fallback_text = editors
13391            .into_iter()
13392            .find(|new_editor| new_editor != test_editor_cx.view())
13393            .expect("Should have one non-test editor now")
13394            .read(test_editor_cx)
13395            .text(test_editor_cx);
13396        assert_eq!(
13397            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13398            "Should use the range from the references response and not the GoToDefinition one"
13399        );
13400    });
13401}
13402
13403#[gpui::test]
13404async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13405    init_test(cx, |_| {});
13406
13407    let language = Arc::new(Language::new(
13408        LanguageConfig::default(),
13409        Some(tree_sitter_rust::LANGUAGE.into()),
13410    ));
13411
13412    let text = r#"
13413        #[cfg(test)]
13414        mod tests() {
13415            #[test]
13416            fn runnable_1() {
13417                let a = 1;
13418            }
13419
13420            #[test]
13421            fn runnable_2() {
13422                let a = 1;
13423                let b = 2;
13424            }
13425        }
13426    "#
13427    .unindent();
13428
13429    let fs = FakeFs::new(cx.executor());
13430    fs.insert_file("/file.rs", Default::default()).await;
13431
13432    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13433    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13434    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13435    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13436    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13437
13438    let editor = cx.new_view(|cx| {
13439        Editor::new(
13440            EditorMode::Full,
13441            multi_buffer,
13442            Some(project.clone()),
13443            true,
13444            cx,
13445        )
13446    });
13447
13448    editor.update(cx, |editor, cx| {
13449        editor.tasks.insert(
13450            (buffer.read(cx).remote_id(), 3),
13451            RunnableTasks {
13452                templates: vec![],
13453                offset: MultiBufferOffset(43),
13454                column: 0,
13455                extra_variables: HashMap::default(),
13456                context_range: BufferOffset(43)..BufferOffset(85),
13457            },
13458        );
13459        editor.tasks.insert(
13460            (buffer.read(cx).remote_id(), 8),
13461            RunnableTasks {
13462                templates: vec![],
13463                offset: MultiBufferOffset(86),
13464                column: 0,
13465                extra_variables: HashMap::default(),
13466                context_range: BufferOffset(86)..BufferOffset(191),
13467            },
13468        );
13469
13470        // Test finding task when cursor is inside function body
13471        editor.change_selections(None, cx, |s| {
13472            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13473        });
13474        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13475        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13476
13477        // Test finding task when cursor is on function name
13478        editor.change_selections(None, cx, |s| {
13479            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13480        });
13481        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13482        assert_eq!(row, 8, "Should find task when cursor is on function name");
13483    });
13484}
13485
13486fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13487    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13488    point..point
13489}
13490
13491fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13492    let (text, ranges) = marked_text_ranges(marked_text, true);
13493    assert_eq!(view.text(cx), text);
13494    assert_eq!(
13495        view.selections.ranges(cx),
13496        ranges,
13497        "Assert selections are {}",
13498        marked_text
13499    );
13500}
13501
13502pub fn handle_signature_help_request(
13503    cx: &mut EditorLspTestContext,
13504    mocked_response: lsp::SignatureHelp,
13505) -> impl Future<Output = ()> {
13506    let mut request =
13507        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13508            let mocked_response = mocked_response.clone();
13509            async move { Ok(Some(mocked_response)) }
13510        });
13511
13512    async move {
13513        request.next().await;
13514    }
13515}
13516
13517/// Handle completion request passing a marked string specifying where the completion
13518/// should be triggered from using '|' character, what range should be replaced, and what completions
13519/// should be returned using '<' and '>' to delimit the range
13520pub fn handle_completion_request(
13521    cx: &mut EditorLspTestContext,
13522    marked_string: &str,
13523    completions: Vec<&'static str>,
13524    counter: Arc<AtomicUsize>,
13525) -> impl Future<Output = ()> {
13526    let complete_from_marker: TextRangeMarker = '|'.into();
13527    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13528    let (_, mut marked_ranges) = marked_text_ranges_by(
13529        marked_string,
13530        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13531    );
13532
13533    let complete_from_position =
13534        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13535    let replace_range =
13536        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13537
13538    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13539        let completions = completions.clone();
13540        counter.fetch_add(1, atomic::Ordering::Release);
13541        async move {
13542            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13543            assert_eq!(
13544                params.text_document_position.position,
13545                complete_from_position
13546            );
13547            Ok(Some(lsp::CompletionResponse::Array(
13548                completions
13549                    .iter()
13550                    .map(|completion_text| lsp::CompletionItem {
13551                        label: completion_text.to_string(),
13552                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13553                            range: replace_range,
13554                            new_text: completion_text.to_string(),
13555                        })),
13556                        ..Default::default()
13557                    })
13558                    .collect(),
13559            )))
13560        }
13561    });
13562
13563    async move {
13564        request.next().await;
13565    }
13566}
13567
13568fn handle_resolve_completion_request(
13569    cx: &mut EditorLspTestContext,
13570    edits: Option<Vec<(&'static str, &'static str)>>,
13571) -> impl Future<Output = ()> {
13572    let edits = edits.map(|edits| {
13573        edits
13574            .iter()
13575            .map(|(marked_string, new_text)| {
13576                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13577                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13578                lsp::TextEdit::new(replace_range, new_text.to_string())
13579            })
13580            .collect::<Vec<_>>()
13581    });
13582
13583    let mut request =
13584        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13585            let edits = edits.clone();
13586            async move {
13587                Ok(lsp::CompletionItem {
13588                    additional_text_edits: edits,
13589                    ..Default::default()
13590                })
13591            }
13592        });
13593
13594    async move {
13595        request.next().await;
13596    }
13597}
13598
13599pub(crate) fn update_test_language_settings(
13600    cx: &mut TestAppContext,
13601    f: impl Fn(&mut AllLanguageSettingsContent),
13602) {
13603    cx.update(|cx| {
13604        SettingsStore::update_global(cx, |store, cx| {
13605            store.update_user_settings::<AllLanguageSettings>(cx, f);
13606        });
13607    });
13608}
13609
13610pub(crate) fn update_test_project_settings(
13611    cx: &mut TestAppContext,
13612    f: impl Fn(&mut ProjectSettings),
13613) {
13614    cx.update(|cx| {
13615        SettingsStore::update_global(cx, |store, cx| {
13616            store.update_user_settings::<ProjectSettings>(cx, f);
13617        });
13618    });
13619}
13620
13621pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13622    cx.update(|cx| {
13623        assets::Assets.load_test_fonts(cx);
13624        let store = SettingsStore::test(cx);
13625        cx.set_global(store);
13626        theme::init(theme::LoadThemes::JustBase, cx);
13627        release_channel::init(SemanticVersion::default(), cx);
13628        client::init_settings(cx);
13629        language::init(cx);
13630        Project::init_settings(cx);
13631        workspace::init_settings(cx);
13632        crate::init(cx);
13633    });
13634
13635    update_test_language_settings(cx, f);
13636}
13637
13638pub(crate) fn rust_lang() -> Arc<Language> {
13639    Arc::new(Language::new(
13640        LanguageConfig {
13641            name: "Rust".into(),
13642            matcher: LanguageMatcher {
13643                path_suffixes: vec!["rs".to_string()],
13644                ..Default::default()
13645            },
13646            ..Default::default()
13647        },
13648        Some(tree_sitter_rust::LANGUAGE.into()),
13649    ))
13650}
13651
13652#[track_caller]
13653fn assert_hunk_revert(
13654    not_reverted_text_with_selections: &str,
13655    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13656    expected_reverted_text_with_selections: &str,
13657    base_text: &str,
13658    cx: &mut EditorLspTestContext,
13659) {
13660    cx.set_state(not_reverted_text_with_selections);
13661    cx.update_editor(|editor, cx| {
13662        editor
13663            .buffer()
13664            .read(cx)
13665            .as_singleton()
13666            .unwrap()
13667            .update(cx, |buffer, cx| {
13668                buffer.set_diff_base(Some(base_text.into()), cx);
13669            });
13670    });
13671    cx.executor().run_until_parked();
13672
13673    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13674        let snapshot = editor.buffer().read(cx).snapshot(cx);
13675        let reverted_hunk_statuses = snapshot
13676            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13677            .map(|hunk| hunk_status(&hunk))
13678            .collect::<Vec<_>>();
13679
13680        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13681        reverted_hunk_statuses
13682    });
13683    cx.executor().run_until_parked();
13684    cx.assert_editor_state(expected_reverted_text_with_selections);
13685    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13686}