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 buffer_diff::{BufferDiff, DiffHunkStatus, DiffHunkStatusKind};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::IndentGuide;
   28use parking_lot::Mutex;
   29use pretty_assertions::{assert_eq, assert_ne};
   30use project::project_settings::{LspSettings, ProjectSettings};
   31use project::FakeFs;
   32use serde_json::{self, json};
   33use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   34use std::{
   35    iter,
   36    sync::atomic::{self, AtomicUsize},
   37};
   38use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   39use unindent::Unindent;
   40use util::{
   41    assert_set_eq, path,
   42    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   43    uri,
   44};
   45use workspace::{
   46    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   47    NavigationEntry, ViewId,
   48};
   49
   50#[gpui::test]
   51fn test_edit_events(cx: &mut TestAppContext) {
   52    init_test(cx, |_| {});
   53
   54    let buffer = cx.new(|cx| {
   55        let mut buffer = language::Buffer::local("123456", cx);
   56        buffer.set_group_interval(Duration::from_secs(1));
   57        buffer
   58    });
   59
   60    let events = Rc::new(RefCell::new(Vec::new()));
   61    let editor1 = cx.add_window({
   62        let events = events.clone();
   63        |window, cx| {
   64            let entity = cx.entity().clone();
   65            cx.subscribe_in(
   66                &entity,
   67                window,
   68                move |_, _, event: &EditorEvent, _, _| match event {
   69                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   70                    EditorEvent::BufferEdited => {
   71                        events.borrow_mut().push(("editor1", "buffer edited"))
   72                    }
   73                    _ => {}
   74                },
   75            )
   76            .detach();
   77            Editor::for_buffer(buffer.clone(), None, window, cx)
   78        }
   79    });
   80
   81    let editor2 = cx.add_window({
   82        let events = events.clone();
   83        |window, cx| {
   84            cx.subscribe_in(
   85                &cx.entity().clone(),
   86                window,
   87                move |_, _, event: &EditorEvent, _, _| match event {
   88                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   89                    EditorEvent::BufferEdited => {
   90                        events.borrow_mut().push(("editor2", "buffer edited"))
   91                    }
   92                    _ => {}
   93                },
   94            )
   95            .detach();
   96            Editor::for_buffer(buffer.clone(), None, window, cx)
   97        }
   98    });
   99
  100    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  101
  102    // Mutating editor 1 will emit an `Edited` event only for that editor.
  103    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  104    assert_eq!(
  105        mem::take(&mut *events.borrow_mut()),
  106        [
  107            ("editor1", "edited"),
  108            ("editor1", "buffer edited"),
  109            ("editor2", "buffer edited"),
  110        ]
  111    );
  112
  113    // Mutating editor 2 will emit an `Edited` event only for that editor.
  114    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  115    assert_eq!(
  116        mem::take(&mut *events.borrow_mut()),
  117        [
  118            ("editor2", "edited"),
  119            ("editor1", "buffer edited"),
  120            ("editor2", "buffer edited"),
  121        ]
  122    );
  123
  124    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  125    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  126    assert_eq!(
  127        mem::take(&mut *events.borrow_mut()),
  128        [
  129            ("editor1", "edited"),
  130            ("editor1", "buffer edited"),
  131            ("editor2", "buffer edited"),
  132        ]
  133    );
  134
  135    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  136    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  137    assert_eq!(
  138        mem::take(&mut *events.borrow_mut()),
  139        [
  140            ("editor1", "edited"),
  141            ("editor1", "buffer edited"),
  142            ("editor2", "buffer edited"),
  143        ]
  144    );
  145
  146    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  147    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  148    assert_eq!(
  149        mem::take(&mut *events.borrow_mut()),
  150        [
  151            ("editor2", "edited"),
  152            ("editor1", "buffer edited"),
  153            ("editor2", "buffer edited"),
  154        ]
  155    );
  156
  157    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  158    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  159    assert_eq!(
  160        mem::take(&mut *events.borrow_mut()),
  161        [
  162            ("editor2", "edited"),
  163            ("editor1", "buffer edited"),
  164            ("editor2", "buffer edited"),
  165        ]
  166    );
  167
  168    // No event is emitted when the mutation is a no-op.
  169    _ = editor2.update(cx, |editor, window, cx| {
  170        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  171
  172        editor.backspace(&Backspace, window, cx);
  173    });
  174    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  175}
  176
  177#[gpui::test]
  178fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  179    init_test(cx, |_| {});
  180
  181    let mut now = Instant::now();
  182    let group_interval = Duration::from_millis(1);
  183    let buffer = cx.new(|cx| {
  184        let mut buf = language::Buffer::local("123456", cx);
  185        buf.set_group_interval(group_interval);
  186        buf
  187    });
  188    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  189    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  190
  191    _ = editor.update(cx, |editor, window, cx| {
  192        editor.start_transaction_at(now, window, cx);
  193        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  194
  195        editor.insert("cd", window, cx);
  196        editor.end_transaction_at(now, cx);
  197        assert_eq!(editor.text(cx), "12cd56");
  198        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  199
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  202        editor.insert("e", window, cx);
  203        editor.end_transaction_at(now, cx);
  204        assert_eq!(editor.text(cx), "12cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  206
  207        now += group_interval + Duration::from_millis(1);
  208        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  209
  210        // Simulate an edit in another editor
  211        buffer.update(cx, |buffer, cx| {
  212            buffer.start_transaction_at(now, cx);
  213            buffer.edit([(0..1, "a")], None, cx);
  214            buffer.edit([(1..1, "b")], None, cx);
  215            buffer.end_transaction_at(now, cx);
  216        });
  217
  218        assert_eq!(editor.text(cx), "ab2cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  220
  221        // Last transaction happened past the group interval in a different editor.
  222        // Undo it individually and don't restore selections.
  223        editor.undo(&Undo, window, cx);
  224        assert_eq!(editor.text(cx), "12cde6");
  225        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  226
  227        // First two transactions happened within the group interval in this editor.
  228        // Undo them together and restore selections.
  229        editor.undo(&Undo, window, cx);
  230        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  231        assert_eq!(editor.text(cx), "123456");
  232        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  233
  234        // Redo the first two transactions together.
  235        editor.redo(&Redo, window, cx);
  236        assert_eq!(editor.text(cx), "12cde6");
  237        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  238
  239        // Redo the last transaction on its own.
  240        editor.redo(&Redo, window, cx);
  241        assert_eq!(editor.text(cx), "ab2cde6");
  242        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  243
  244        // Test empty transactions.
  245        editor.start_transaction_at(now, window, cx);
  246        editor.end_transaction_at(now, cx);
  247        editor.undo(&Undo, window, cx);
  248        assert_eq!(editor.text(cx), "12cde6");
  249    });
  250}
  251
  252#[gpui::test]
  253fn test_ime_composition(cx: &mut TestAppContext) {
  254    init_test(cx, |_| {});
  255
  256    let buffer = cx.new(|cx| {
  257        let mut buffer = language::Buffer::local("abcde", cx);
  258        // Ensure automatic grouping doesn't occur.
  259        buffer.set_group_interval(Duration::ZERO);
  260        buffer
  261    });
  262
  263    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  264    cx.add_window(|window, cx| {
  265        let mut editor = build_editor(buffer.clone(), window, cx);
  266
  267        // Start a new IME composition.
  268        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  269        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  270        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  271        assert_eq!(editor.text(cx), "äbcde");
  272        assert_eq!(
  273            editor.marked_text_ranges(cx),
  274            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  275        );
  276
  277        // Finalize IME composition.
  278        editor.replace_text_in_range(None, "ā", window, cx);
  279        assert_eq!(editor.text(cx), "ābcde");
  280        assert_eq!(editor.marked_text_ranges(cx), None);
  281
  282        // IME composition edits are grouped and are undone/redone at once.
  283        editor.undo(&Default::default(), window, cx);
  284        assert_eq!(editor.text(cx), "abcde");
  285        assert_eq!(editor.marked_text_ranges(cx), None);
  286        editor.redo(&Default::default(), window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // Start a new IME composition.
  291        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  295        );
  296
  297        // Undoing during an IME composition cancels it.
  298        editor.undo(&Default::default(), window, cx);
  299        assert_eq!(editor.text(cx), "ābcde");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  303        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  304        assert_eq!(editor.text(cx), "ābcdè");
  305        assert_eq!(
  306            editor.marked_text_ranges(cx),
  307            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  308        );
  309
  310        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  311        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  312        assert_eq!(editor.text(cx), "ābcdę");
  313        assert_eq!(editor.marked_text_ranges(cx), None);
  314
  315        // Start a new IME composition with multiple cursors.
  316        editor.change_selections(None, window, cx, |s| {
  317            s.select_ranges([
  318                OffsetUtf16(1)..OffsetUtf16(1),
  319                OffsetUtf16(3)..OffsetUtf16(3),
  320                OffsetUtf16(5)..OffsetUtf16(5),
  321            ])
  322        });
  323        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  324        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  325        assert_eq!(
  326            editor.marked_text_ranges(cx),
  327            Some(vec![
  328                OffsetUtf16(0)..OffsetUtf16(3),
  329                OffsetUtf16(4)..OffsetUtf16(7),
  330                OffsetUtf16(8)..OffsetUtf16(11)
  331            ])
  332        );
  333
  334        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  335        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  336        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  337        assert_eq!(
  338            editor.marked_text_ranges(cx),
  339            Some(vec![
  340                OffsetUtf16(1)..OffsetUtf16(2),
  341                OffsetUtf16(5)..OffsetUtf16(6),
  342                OffsetUtf16(9)..OffsetUtf16(10)
  343            ])
  344        );
  345
  346        // Finalize IME composition with multiple cursors.
  347        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  348        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  349        assert_eq!(editor.marked_text_ranges(cx), None);
  350
  351        editor
  352    });
  353}
  354
  355#[gpui::test]
  356fn test_selection_with_mouse(cx: &mut TestAppContext) {
  357    init_test(cx, |_| {});
  358
  359    let editor = cx.add_window(|window, cx| {
  360        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  361        build_editor(buffer, window, cx)
  362    });
  363
  364    _ = editor.update(cx, |editor, window, cx| {
  365        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  366    });
  367    assert_eq!(
  368        editor
  369            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  370            .unwrap(),
  371        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  372    );
  373
  374    _ = editor.update(cx, |editor, window, cx| {
  375        editor.update_selection(
  376            DisplayPoint::new(DisplayRow(3), 3),
  377            0,
  378            gpui::Point::<f32>::default(),
  379            window,
  380            cx,
  381        );
  382    });
  383
  384    assert_eq!(
  385        editor
  386            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  387            .unwrap(),
  388        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  389    );
  390
  391    _ = editor.update(cx, |editor, window, cx| {
  392        editor.update_selection(
  393            DisplayPoint::new(DisplayRow(1), 1),
  394            0,
  395            gpui::Point::<f32>::default(),
  396            window,
  397            cx,
  398        );
  399    });
  400
  401    assert_eq!(
  402        editor
  403            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  404            .unwrap(),
  405        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  406    );
  407
  408    _ = editor.update(cx, |editor, window, cx| {
  409        editor.end_selection(window, cx);
  410        editor.update_selection(
  411            DisplayPoint::new(DisplayRow(3), 3),
  412            0,
  413            gpui::Point::<f32>::default(),
  414            window,
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  422            .unwrap(),
  423        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  424    );
  425
  426    _ = editor.update(cx, |editor, window, cx| {
  427        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  428        editor.update_selection(
  429            DisplayPoint::new(DisplayRow(0), 0),
  430            0,
  431            gpui::Point::<f32>::default(),
  432            window,
  433            cx,
  434        );
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  440            .unwrap(),
  441        [
  442            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  443            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  444        ]
  445    );
  446
  447    _ = editor.update(cx, |editor, window, cx| {
  448        editor.end_selection(window, cx);
  449    });
  450
  451    assert_eq!(
  452        editor
  453            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  454            .unwrap(),
  455        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  456    );
  457}
  458
  459#[gpui::test]
  460fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  461    init_test(cx, |_| {});
  462
  463    let editor = cx.add_window(|window, cx| {
  464        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  465        build_editor(buffer, window, cx)
  466    });
  467
  468    _ = editor.update(cx, |editor, window, cx| {
  469        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  470    });
  471
  472    _ = editor.update(cx, |editor, window, cx| {
  473        editor.end_selection(window, cx);
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  487            .unwrap(),
  488        [
  489            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  490            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  491        ]
  492    );
  493
  494    _ = editor.update(cx, |editor, window, cx| {
  495        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  496    });
  497
  498    _ = editor.update(cx, |editor, window, cx| {
  499        editor.end_selection(window, cx);
  500    });
  501
  502    assert_eq!(
  503        editor
  504            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  505            .unwrap(),
  506        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  507    );
  508}
  509
  510#[gpui::test]
  511fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  512    init_test(cx, |_| {});
  513
  514    let editor = cx.add_window(|window, cx| {
  515        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  516        build_editor(buffer, window, cx)
  517    });
  518
  519    _ = editor.update(cx, |editor, window, cx| {
  520        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  521        assert_eq!(
  522            editor.selections.display_ranges(cx),
  523            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  524        );
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.update_selection(
  529            DisplayPoint::new(DisplayRow(3), 3),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            window,
  533            cx,
  534        );
  535        assert_eq!(
  536            editor.selections.display_ranges(cx),
  537            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  538        );
  539    });
  540
  541    _ = editor.update(cx, |editor, window, cx| {
  542        editor.cancel(&Cancel, window, cx);
  543        editor.update_selection(
  544            DisplayPoint::new(DisplayRow(1), 1),
  545            0,
  546            gpui::Point::<f32>::default(),
  547            window,
  548            cx,
  549        );
  550        assert_eq!(
  551            editor.selections.display_ranges(cx),
  552            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  553        );
  554    });
  555}
  556
  557#[gpui::test]
  558fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  559    init_test(cx, |_| {});
  560
  561    let editor = cx.add_window(|window, cx| {
  562        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  563        build_editor(buffer, window, cx)
  564    });
  565
  566    _ = editor.update(cx, |editor, window, cx| {
  567        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  568        assert_eq!(
  569            editor.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        editor.move_down(&Default::default(), window, cx);
  574        assert_eq!(
  575            editor.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  577        );
  578
  579        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  580        assert_eq!(
  581            editor.selections.display_ranges(cx),
  582            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  583        );
  584
  585        editor.move_up(&Default::default(), window, cx);
  586        assert_eq!(
  587            editor.selections.display_ranges(cx),
  588            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  589        );
  590    });
  591}
  592
  593#[gpui::test]
  594fn test_clone(cx: &mut TestAppContext) {
  595    init_test(cx, |_| {});
  596
  597    let (text, selection_ranges) = marked_text_ranges(
  598        indoc! {"
  599            one
  600            two
  601            threeˇ
  602            four
  603            fiveˇ
  604        "},
  605        true,
  606    );
  607
  608    let editor = cx.add_window(|window, cx| {
  609        let buffer = MultiBuffer::build_simple(&text, cx);
  610        build_editor(buffer, window, cx)
  611    });
  612
  613    _ = editor.update(cx, |editor, window, cx| {
  614        editor.change_selections(None, window, cx, |s| {
  615            s.select_ranges(selection_ranges.clone())
  616        });
  617        editor.fold_creases(
  618            vec![
  619                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  620                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  621            ],
  622            true,
  623            window,
  624            cx,
  625        );
  626    });
  627
  628    let cloned_editor = editor
  629        .update(cx, |editor, _, cx| {
  630            cx.open_window(Default::default(), |window, cx| {
  631                cx.new(|cx| editor.clone(window, cx))
  632            })
  633        })
  634        .unwrap()
  635        .unwrap();
  636
  637    let snapshot = editor
  638        .update(cx, |e, window, cx| e.snapshot(window, cx))
  639        .unwrap();
  640    let cloned_snapshot = cloned_editor
  641        .update(cx, |e, window, cx| e.snapshot(window, cx))
  642        .unwrap();
  643
  644    assert_eq!(
  645        cloned_editor
  646            .update(cx, |e, _, cx| e.display_text(cx))
  647            .unwrap(),
  648        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  649    );
  650    assert_eq!(
  651        cloned_snapshot
  652            .folds_in_range(0..text.len())
  653            .collect::<Vec<_>>(),
  654        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  655    );
  656    assert_set_eq!(
  657        cloned_editor
  658            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  659            .unwrap(),
  660        editor
  661            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  662            .unwrap()
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  670            .unwrap()
  671    );
  672}
  673
  674#[gpui::test]
  675async fn test_navigation_history(cx: &mut TestAppContext) {
  676    init_test(cx, |_| {});
  677
  678    use workspace::item::Item;
  679
  680    let fs = FakeFs::new(cx.executor());
  681    let project = Project::test(fs, [], cx).await;
  682    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  683    let pane = workspace
  684        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  685        .unwrap();
  686
  687    _ = workspace.update(cx, |_v, window, cx| {
  688        cx.new(|cx| {
  689            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  690            let mut editor = build_editor(buffer.clone(), window, cx);
  691            let handle = cx.entity();
  692            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  693
  694            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  695                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  696            }
  697
  698            // Move the cursor a small distance.
  699            // Nothing is added to the navigation history.
  700            editor.change_selections(None, window, cx, |s| {
  701                s.select_display_ranges([
  702                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  703                ])
  704            });
  705            editor.change_selections(None, window, cx, |s| {
  706                s.select_display_ranges([
  707                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  708                ])
  709            });
  710            assert!(pop_history(&mut editor, cx).is_none());
  711
  712            // Move the cursor a large distance.
  713            // The history can jump back to the previous position.
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  717                ])
  718            });
  719            let nav_entry = pop_history(&mut editor, cx).unwrap();
  720            editor.navigate(nav_entry.data.unwrap(), window, cx);
  721            assert_eq!(nav_entry.item.id(), cx.entity_id());
  722            assert_eq!(
  723                editor.selections.display_ranges(cx),
  724                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  725            );
  726            assert!(pop_history(&mut editor, cx).is_none());
  727
  728            // Move the cursor a small distance via the mouse.
  729            // Nothing is added to the navigation history.
  730            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  731            editor.end_selection(window, cx);
  732            assert_eq!(
  733                editor.selections.display_ranges(cx),
  734                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  735            );
  736            assert!(pop_history(&mut editor, cx).is_none());
  737
  738            // Move the cursor a large distance via the mouse.
  739            // The history can jump back to the previous position.
  740            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  741            editor.end_selection(window, cx);
  742            assert_eq!(
  743                editor.selections.display_ranges(cx),
  744                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  745            );
  746            let nav_entry = pop_history(&mut editor, cx).unwrap();
  747            editor.navigate(nav_entry.data.unwrap(), window, cx);
  748            assert_eq!(nav_entry.item.id(), cx.entity_id());
  749            assert_eq!(
  750                editor.selections.display_ranges(cx),
  751                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  752            );
  753            assert!(pop_history(&mut editor, cx).is_none());
  754
  755            // Set scroll position to check later
  756            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  757            let original_scroll_position = editor.scroll_manager.anchor();
  758
  759            // Jump to the end of the document and adjust scroll
  760            editor.move_to_end(&MoveToEnd, window, cx);
  761            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  762            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  763
  764            let nav_entry = pop_history(&mut editor, cx).unwrap();
  765            editor.navigate(nav_entry.data.unwrap(), window, cx);
  766            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  767
  768            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  769            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  770            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  771            let invalid_point = Point::new(9999, 0);
  772            editor.navigate(
  773                Box::new(NavigationData {
  774                    cursor_anchor: invalid_anchor,
  775                    cursor_position: invalid_point,
  776                    scroll_anchor: ScrollAnchor {
  777                        anchor: invalid_anchor,
  778                        offset: Default::default(),
  779                    },
  780                    scroll_top_row: invalid_point.row,
  781                }),
  782                window,
  783                cx,
  784            );
  785            assert_eq!(
  786                editor.selections.display_ranges(cx),
  787                &[editor.max_point(cx)..editor.max_point(cx)]
  788            );
  789            assert_eq!(
  790                editor.scroll_position(cx),
  791                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  792            );
  793
  794            editor
  795        })
  796    });
  797}
  798
  799#[gpui::test]
  800fn test_cancel(cx: &mut TestAppContext) {
  801    init_test(cx, |_| {});
  802
  803    let editor = cx.add_window(|window, cx| {
  804        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  805        build_editor(buffer, window, cx)
  806    });
  807
  808    _ = editor.update(cx, |editor, window, cx| {
  809        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  810        editor.update_selection(
  811            DisplayPoint::new(DisplayRow(1), 1),
  812            0,
  813            gpui::Point::<f32>::default(),
  814            window,
  815            cx,
  816        );
  817        editor.end_selection(window, cx);
  818
  819        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  820        editor.update_selection(
  821            DisplayPoint::new(DisplayRow(0), 3),
  822            0,
  823            gpui::Point::<f32>::default(),
  824            window,
  825            cx,
  826        );
  827        editor.end_selection(window, cx);
  828        assert_eq!(
  829            editor.selections.display_ranges(cx),
  830            [
  831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  832                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  833            ]
  834        );
  835    });
  836
  837    _ = editor.update(cx, |editor, window, cx| {
  838        editor.cancel(&Cancel, window, cx);
  839        assert_eq!(
  840            editor.selections.display_ranges(cx),
  841            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852}
  853
  854#[gpui::test]
  855fn test_fold_action(cx: &mut TestAppContext) {
  856    init_test(cx, |_| {});
  857
  858    let editor = cx.add_window(|window, cx| {
  859        let buffer = MultiBuffer::build_simple(
  860            &"
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {
  869                        2
  870                    }
  871
  872                    fn c() {
  873                        3
  874                    }
  875                }
  876            "
  877            .unindent(),
  878            cx,
  879        );
  880        build_editor(buffer.clone(), window, cx)
  881    });
  882
  883    _ = editor.update(cx, |editor, window, cx| {
  884        editor.change_selections(None, window, cx, |s| {
  885            s.select_display_ranges([
  886                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  887            ]);
  888        });
  889        editor.fold(&Fold, window, cx);
  890        assert_eq!(
  891            editor.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        editor.fold(&Fold, window, cx);
  911        assert_eq!(
  912            editor.display_text(cx),
  913            "
  914                impl Foo {⋯
  915                }
  916            "
  917            .unindent(),
  918        );
  919
  920        editor.unfold_lines(&UnfoldLines, window, cx);
  921        assert_eq!(
  922            editor.display_text(cx),
  923            "
  924                impl Foo {
  925                    // Hello!
  926
  927                    fn a() {
  928                        1
  929                    }
  930
  931                    fn b() {⋯
  932                    }
  933
  934                    fn c() {⋯
  935                    }
  936                }
  937            "
  938            .unindent(),
  939        );
  940
  941        editor.unfold_lines(&UnfoldLines, window, cx);
  942        assert_eq!(
  943            editor.display_text(cx),
  944            editor.buffer.read(cx).read(cx).text()
  945        );
  946    });
  947}
  948
  949#[gpui::test]
  950fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  951    init_test(cx, |_| {});
  952
  953    let editor = cx.add_window(|window, cx| {
  954        let buffer = MultiBuffer::build_simple(
  955            &"
  956                class Foo:
  957                    # Hello!
  958
  959                    def a():
  960                        print(1)
  961
  962                    def b():
  963                        print(2)
  964
  965                    def c():
  966                        print(3)
  967            "
  968            .unindent(),
  969            cx,
  970        );
  971        build_editor(buffer.clone(), window, cx)
  972    });
  973
  974    _ = editor.update(cx, |editor, window, cx| {
  975        editor.change_selections(None, window, cx, |s| {
  976            s.select_display_ranges([
  977                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  978            ]);
  979        });
  980        editor.fold(&Fold, window, cx);
  981        assert_eq!(
  982            editor.display_text(cx),
  983            "
  984                class Foo:
  985                    # Hello!
  986
  987                    def a():
  988                        print(1)
  989
  990                    def b():⋯
  991
  992                    def c():⋯
  993            "
  994            .unindent(),
  995        );
  996
  997        editor.fold(&Fold, window, cx);
  998        assert_eq!(
  999            editor.display_text(cx),
 1000            "
 1001                class Foo:⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.unfold_lines(&UnfoldLines, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:
 1011                    # Hello!
 1012
 1013                    def a():
 1014                        print(1)
 1015
 1016                    def b():⋯
 1017
 1018                    def c():⋯
 1019            "
 1020            .unindent(),
 1021        );
 1022
 1023        editor.unfold_lines(&UnfoldLines, window, cx);
 1024        assert_eq!(
 1025            editor.display_text(cx),
 1026            editor.buffer.read(cx).read(cx).text()
 1027        );
 1028    });
 1029}
 1030
 1031#[gpui::test]
 1032fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1033    init_test(cx, |_| {});
 1034
 1035    let editor = cx.add_window(|window, cx| {
 1036        let buffer = MultiBuffer::build_simple(
 1037            &"
 1038                class Foo:
 1039                    # Hello!
 1040
 1041                    def a():
 1042                        print(1)
 1043
 1044                    def b():
 1045                        print(2)
 1046
 1047
 1048                    def c():
 1049                        print(3)
 1050
 1051
 1052            "
 1053            .unindent(),
 1054            cx,
 1055        );
 1056        build_editor(buffer.clone(), window, cx)
 1057    });
 1058
 1059    _ = editor.update(cx, |editor, window, cx| {
 1060        editor.change_selections(None, window, cx, |s| {
 1061            s.select_display_ranges([
 1062                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1063            ]);
 1064        });
 1065        editor.fold(&Fold, window, cx);
 1066        assert_eq!(
 1067            editor.display_text(cx),
 1068            "
 1069                class Foo:
 1070                    # Hello!
 1071
 1072                    def a():
 1073                        print(1)
 1074
 1075                    def b():⋯
 1076
 1077
 1078                    def c():⋯
 1079
 1080
 1081            "
 1082            .unindent(),
 1083        );
 1084
 1085        editor.fold(&Fold, window, cx);
 1086        assert_eq!(
 1087            editor.display_text(cx),
 1088            "
 1089                class Foo:⋯
 1090
 1091
 1092            "
 1093            .unindent(),
 1094        );
 1095
 1096        editor.unfold_lines(&UnfoldLines, window, cx);
 1097        assert_eq!(
 1098            editor.display_text(cx),
 1099            "
 1100                class Foo:
 1101                    # Hello!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():⋯
 1107
 1108
 1109                    def c():⋯
 1110
 1111
 1112            "
 1113            .unindent(),
 1114        );
 1115
 1116        editor.unfold_lines(&UnfoldLines, window, cx);
 1117        assert_eq!(
 1118            editor.display_text(cx),
 1119            editor.buffer.read(cx).read(cx).text()
 1120        );
 1121    });
 1122}
 1123
 1124#[gpui::test]
 1125fn test_fold_at_level(cx: &mut TestAppContext) {
 1126    init_test(cx, |_| {});
 1127
 1128    let editor = cx.add_window(|window, cx| {
 1129        let buffer = MultiBuffer::build_simple(
 1130            &"
 1131                class Foo:
 1132                    # Hello!
 1133
 1134                    def a():
 1135                        print(1)
 1136
 1137                    def b():
 1138                        print(2)
 1139
 1140
 1141                class Bar:
 1142                    # World!
 1143
 1144                    def a():
 1145                        print(1)
 1146
 1147                    def b():
 1148                        print(2)
 1149
 1150
 1151            "
 1152            .unindent(),
 1153            cx,
 1154        );
 1155        build_editor(buffer.clone(), window, cx)
 1156    });
 1157
 1158    _ = editor.update(cx, |editor, window, cx| {
 1159        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1160        assert_eq!(
 1161            editor.display_text(cx),
 1162            "
 1163                class Foo:
 1164                    # Hello!
 1165
 1166                    def a():⋯
 1167
 1168                    def b():⋯
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179            "
 1180            .unindent(),
 1181        );
 1182
 1183        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1184        assert_eq!(
 1185            editor.display_text(cx),
 1186            "
 1187                class Foo:⋯
 1188
 1189
 1190                class Bar:⋯
 1191
 1192
 1193            "
 1194            .unindent(),
 1195        );
 1196
 1197        editor.unfold_all(&UnfoldAll, window, cx);
 1198        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1199        assert_eq!(
 1200            editor.display_text(cx),
 1201            "
 1202                class Foo:
 1203                    # Hello!
 1204
 1205                    def a():
 1206                        print(1)
 1207
 1208                    def b():
 1209                        print(2)
 1210
 1211
 1212                class Bar:
 1213                    # World!
 1214
 1215                    def a():
 1216                        print(1)
 1217
 1218                    def b():
 1219                        print(2)
 1220
 1221
 1222            "
 1223            .unindent(),
 1224        );
 1225
 1226        assert_eq!(
 1227            editor.display_text(cx),
 1228            editor.buffer.read(cx).read(cx).text()
 1229        );
 1230    });
 1231}
 1232
 1233#[gpui::test]
 1234fn test_move_cursor(cx: &mut TestAppContext) {
 1235    init_test(cx, |_| {});
 1236
 1237    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1238    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1239
 1240    buffer.update(cx, |buffer, cx| {
 1241        buffer.edit(
 1242            vec![
 1243                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1244                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1245            ],
 1246            None,
 1247            cx,
 1248        );
 1249    });
 1250    _ = editor.update(cx, |editor, window, cx| {
 1251        assert_eq!(
 1252            editor.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        editor.move_down(&MoveDown, window, cx);
 1257        assert_eq!(
 1258            editor.selections.display_ranges(cx),
 1259            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1260        );
 1261
 1262        editor.move_right(&MoveRight, window, cx);
 1263        assert_eq!(
 1264            editor.selections.display_ranges(cx),
 1265            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1266        );
 1267
 1268        editor.move_left(&MoveLeft, window, cx);
 1269        assert_eq!(
 1270            editor.selections.display_ranges(cx),
 1271            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1272        );
 1273
 1274        editor.move_up(&MoveUp, window, cx);
 1275        assert_eq!(
 1276            editor.selections.display_ranges(cx),
 1277            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1278        );
 1279
 1280        editor.move_to_end(&MoveToEnd, window, cx);
 1281        assert_eq!(
 1282            editor.selections.display_ranges(cx),
 1283            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1284        );
 1285
 1286        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1287        assert_eq!(
 1288            editor.selections.display_ranges(cx),
 1289            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1290        );
 1291
 1292        editor.change_selections(None, window, cx, |s| {
 1293            s.select_display_ranges([
 1294                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1295            ]);
 1296        });
 1297        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1298        assert_eq!(
 1299            editor.selections.display_ranges(cx),
 1300            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1301        );
 1302
 1303        editor.select_to_end(&SelectToEnd, window, cx);
 1304        assert_eq!(
 1305            editor.selections.display_ranges(cx),
 1306            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1307        );
 1308    });
 1309}
 1310
 1311// TODO: Re-enable this test
 1312#[cfg(target_os = "macos")]
 1313#[gpui::test]
 1314fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1315    init_test(cx, |_| {});
 1316
 1317    let editor = cx.add_window(|window, cx| {
 1318        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1319        build_editor(buffer.clone(), window, cx)
 1320    });
 1321
 1322    assert_eq!('🟥'.len_utf8(), 4);
 1323    assert_eq!('α'.len_utf8(), 2);
 1324
 1325    _ = editor.update(cx, |editor, window, cx| {
 1326        editor.fold_creases(
 1327            vec![
 1328                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1329                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1330                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1331            ],
 1332            true,
 1333            window,
 1334            cx,
 1335        );
 1336        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1337
 1338        editor.move_right(&MoveRight, window, cx);
 1339        assert_eq!(
 1340            editor.selections.display_ranges(cx),
 1341            &[empty_range(0, "🟥".len())]
 1342        );
 1343        editor.move_right(&MoveRight, window, cx);
 1344        assert_eq!(
 1345            editor.selections.display_ranges(cx),
 1346            &[empty_range(0, "🟥🟧".len())]
 1347        );
 1348        editor.move_right(&MoveRight, window, cx);
 1349        assert_eq!(
 1350            editor.selections.display_ranges(cx),
 1351            &[empty_range(0, "🟥🟧⋯".len())]
 1352        );
 1353
 1354        editor.move_down(&MoveDown, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(1, "ab⋯e".len())]
 1358        );
 1359        editor.move_left(&MoveLeft, window, cx);
 1360        assert_eq!(
 1361            editor.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯".len())]
 1363        );
 1364        editor.move_left(&MoveLeft, window, cx);
 1365        assert_eq!(
 1366            editor.selections.display_ranges(cx),
 1367            &[empty_range(1, "ab".len())]
 1368        );
 1369        editor.move_left(&MoveLeft, window, cx);
 1370        assert_eq!(
 1371            editor.selections.display_ranges(cx),
 1372            &[empty_range(1, "a".len())]
 1373        );
 1374
 1375        editor.move_down(&MoveDown, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(2, "α".len())]
 1379        );
 1380        editor.move_right(&MoveRight, window, cx);
 1381        assert_eq!(
 1382            editor.selections.display_ranges(cx),
 1383            &[empty_range(2, "αβ".len())]
 1384        );
 1385        editor.move_right(&MoveRight, window, cx);
 1386        assert_eq!(
 1387            editor.selections.display_ranges(cx),
 1388            &[empty_range(2, "αβ⋯".len())]
 1389        );
 1390        editor.move_right(&MoveRight, window, cx);
 1391        assert_eq!(
 1392            editor.selections.display_ranges(cx),
 1393            &[empty_range(2, "αβ⋯ε".len())]
 1394        );
 1395
 1396        editor.move_up(&MoveUp, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(1, "ab⋯e".len())]
 1400        );
 1401        editor.move_down(&MoveDown, window, cx);
 1402        assert_eq!(
 1403            editor.selections.display_ranges(cx),
 1404            &[empty_range(2, "αβ⋯ε".len())]
 1405        );
 1406        editor.move_up(&MoveUp, window, cx);
 1407        assert_eq!(
 1408            editor.selections.display_ranges(cx),
 1409            &[empty_range(1, "ab⋯e".len())]
 1410        );
 1411
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(0, "🟥🟧".len())]
 1416        );
 1417        editor.move_left(&MoveLeft, window, cx);
 1418        assert_eq!(
 1419            editor.selections.display_ranges(cx),
 1420            &[empty_range(0, "🟥".len())]
 1421        );
 1422        editor.move_left(&MoveLeft, window, cx);
 1423        assert_eq!(
 1424            editor.selections.display_ranges(cx),
 1425            &[empty_range(0, "".len())]
 1426        );
 1427    });
 1428}
 1429
 1430#[gpui::test]
 1431fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1432    init_test(cx, |_| {});
 1433
 1434    let editor = cx.add_window(|window, cx| {
 1435        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1436        build_editor(buffer.clone(), window, cx)
 1437    });
 1438    _ = editor.update(cx, |editor, window, cx| {
 1439        editor.change_selections(None, window, cx, |s| {
 1440            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1441        });
 1442
 1443        // moving above start of document should move selection to start of document,
 1444        // but the next move down should still be at the original goal_x
 1445        editor.move_up(&MoveUp, window, cx);
 1446        assert_eq!(
 1447            editor.selections.display_ranges(cx),
 1448            &[empty_range(0, "".len())]
 1449        );
 1450
 1451        editor.move_down(&MoveDown, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(1, "abcd".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(2, "αβγ".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(3, "abcd".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1473        );
 1474
 1475        // moving past end of document should not change goal_x
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(5, "".len())]
 1480        );
 1481
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_up(&MoveUp, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(3, "abcd".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(2, "αβγ".len())]
 1504        );
 1505    });
 1506}
 1507
 1508#[gpui::test]
 1509fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1510    init_test(cx, |_| {});
 1511    let move_to_beg = MoveToBeginningOfLine {
 1512        stop_at_soft_wraps: true,
 1513        stop_at_indent: true,
 1514    };
 1515
 1516    let move_to_end = MoveToEndOfLine {
 1517        stop_at_soft_wraps: true,
 1518    };
 1519
 1520    let editor = cx.add_window(|window, cx| {
 1521        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1522        build_editor(buffer, window, cx)
 1523    });
 1524    _ = editor.update(cx, |editor, window, cx| {
 1525        editor.change_selections(None, window, cx, |s| {
 1526            s.select_display_ranges([
 1527                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1528                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1529            ]);
 1530        });
 1531    });
 1532
 1533    _ = editor.update(cx, |editor, window, cx| {
 1534        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1535        assert_eq!(
 1536            editor.selections.display_ranges(cx),
 1537            &[
 1538                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1539                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1540            ]
 1541        );
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_end_of_line(&move_to_end, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1572                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1573            ]
 1574        );
 1575    });
 1576
 1577    // Moving to the end of line again is a no-op.
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_left(&MoveLeft, window, cx);
 1591        editor.select_to_beginning_of_line(
 1592            &SelectToBeginningOfLine {
 1593                stop_at_soft_wraps: true,
 1594                stop_at_indent: true,
 1595            },
 1596            window,
 1597            cx,
 1598        );
 1599        assert_eq!(
 1600            editor.selections.display_ranges(cx),
 1601            &[
 1602                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1603                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1604            ]
 1605        );
 1606    });
 1607
 1608    _ = editor.update(cx, |editor, window, cx| {
 1609        editor.select_to_beginning_of_line(
 1610            &SelectToBeginningOfLine {
 1611                stop_at_soft_wraps: true,
 1612                stop_at_indent: true,
 1613            },
 1614            window,
 1615            cx,
 1616        );
 1617        assert_eq!(
 1618            editor.selections.display_ranges(cx),
 1619            &[
 1620                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1621                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1622            ]
 1623        );
 1624    });
 1625
 1626    _ = editor.update(cx, |editor, window, cx| {
 1627        editor.select_to_beginning_of_line(
 1628            &SelectToBeginningOfLine {
 1629                stop_at_soft_wraps: true,
 1630                stop_at_indent: true,
 1631            },
 1632            window,
 1633            cx,
 1634        );
 1635        assert_eq!(
 1636            editor.selections.display_ranges(cx),
 1637            &[
 1638                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1639                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1640            ]
 1641        );
 1642    });
 1643
 1644    _ = editor.update(cx, |editor, window, cx| {
 1645        editor.select_to_end_of_line(
 1646            &SelectToEndOfLine {
 1647                stop_at_soft_wraps: true,
 1648            },
 1649            window,
 1650            cx,
 1651        );
 1652        assert_eq!(
 1653            editor.selections.display_ranges(cx),
 1654            &[
 1655                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1656                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1657            ]
 1658        );
 1659    });
 1660
 1661    _ = editor.update(cx, |editor, window, cx| {
 1662        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1663        assert_eq!(editor.display_text(cx), "ab\n  de");
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "\n");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1680                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1681            ]
 1682        );
 1683    });
 1684}
 1685
 1686#[gpui::test]
 1687fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1688    init_test(cx, |_| {});
 1689    let move_to_beg = MoveToBeginningOfLine {
 1690        stop_at_soft_wraps: false,
 1691        stop_at_indent: false,
 1692    };
 1693
 1694    let move_to_end = MoveToEndOfLine {
 1695        stop_at_soft_wraps: false,
 1696    };
 1697
 1698    let editor = cx.add_window(|window, cx| {
 1699        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1700        build_editor(buffer, window, cx)
 1701    });
 1702
 1703    _ = editor.update(cx, |editor, window, cx| {
 1704        editor.set_wrap_width(Some(140.0.into()), cx);
 1705
 1706        // We expect the following lines after wrapping
 1707        // ```
 1708        // thequickbrownfox
 1709        // jumpedoverthelazydo
 1710        // gs
 1711        // ```
 1712        // The final `gs` was soft-wrapped onto a new line.
 1713        assert_eq!(
 1714            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1715            editor.display_text(cx),
 1716        );
 1717
 1718        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1719        // Start the cursor at the `k` on the first line
 1720        editor.change_selections(None, window, cx, |s| {
 1721            s.select_display_ranges([
 1722                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1723            ]);
 1724        });
 1725
 1726        // Moving to the beginning of the line should put us at the beginning of the line.
 1727        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1728        assert_eq!(
 1729            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1730            editor.selections.display_ranges(cx)
 1731        );
 1732
 1733        // Moving to the end of the line should put us at the end of the line.
 1734        editor.move_to_end_of_line(&move_to_end, window, cx);
 1735        assert_eq!(
 1736            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1737            editor.selections.display_ranges(cx)
 1738        );
 1739
 1740        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1741        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1742        editor.change_selections(None, window, cx, |s| {
 1743            s.select_display_ranges([
 1744                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1745            ]);
 1746        });
 1747
 1748        // Moving to the beginning of the line should put us at the start of the second line of
 1749        // display text, i.e., the `j`.
 1750        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1751        assert_eq!(
 1752            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1753            editor.selections.display_ranges(cx)
 1754        );
 1755
 1756        // Moving to the beginning of the line again should be a no-op.
 1757        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1758        assert_eq!(
 1759            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1760            editor.selections.display_ranges(cx)
 1761        );
 1762
 1763        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1764        // next display line.
 1765        editor.move_to_end_of_line(&move_to_end, window, cx);
 1766        assert_eq!(
 1767            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1768            editor.selections.display_ranges(cx)
 1769        );
 1770
 1771        // Moving to the end of the line again should be a no-op.
 1772        editor.move_to_end_of_line(&move_to_end, window, cx);
 1773        assert_eq!(
 1774            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1775            editor.selections.display_ranges(cx)
 1776        );
 1777    });
 1778}
 1779
 1780#[gpui::test]
 1781fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1782    init_test(cx, |_| {});
 1783
 1784    let editor = cx.add_window(|window, cx| {
 1785        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1786        build_editor(buffer, window, cx)
 1787    });
 1788    _ = editor.update(cx, |editor, window, cx| {
 1789        editor.change_selections(None, window, cx, |s| {
 1790            s.select_display_ranges([
 1791                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1792                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1793            ])
 1794        });
 1795
 1796        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1797        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1798
 1799        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1800        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1801
 1802        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1803        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1804
 1805        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1806        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1807
 1808        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1809        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1810
 1811        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1812        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1813
 1814        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1815        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1816
 1817        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1818        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1819
 1820        editor.move_right(&MoveRight, window, cx);
 1821        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1822        assert_selection_ranges(
 1823            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1824            editor,
 1825            cx,
 1826        );
 1827
 1828        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1829        assert_selection_ranges(
 1830            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1831            editor,
 1832            cx,
 1833        );
 1834
 1835        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1836        assert_selection_ranges(
 1837            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1838            editor,
 1839            cx,
 1840        );
 1841    });
 1842}
 1843
 1844#[gpui::test]
 1845fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1846    init_test(cx, |_| {});
 1847
 1848    let editor = cx.add_window(|window, cx| {
 1849        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1850        build_editor(buffer, window, cx)
 1851    });
 1852
 1853    _ = editor.update(cx, |editor, window, cx| {
 1854        editor.set_wrap_width(Some(140.0.into()), cx);
 1855        assert_eq!(
 1856            editor.display_text(cx),
 1857            "use one::{\n    two::three::\n    four::five\n};"
 1858        );
 1859
 1860        editor.change_selections(None, window, cx, |s| {
 1861            s.select_display_ranges([
 1862                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1863            ]);
 1864        });
 1865
 1866        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1867        assert_eq!(
 1868            editor.selections.display_ranges(cx),
 1869            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1870        );
 1871
 1872        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1873        assert_eq!(
 1874            editor.selections.display_ranges(cx),
 1875            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1876        );
 1877
 1878        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1879        assert_eq!(
 1880            editor.selections.display_ranges(cx),
 1881            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1882        );
 1883
 1884        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1885        assert_eq!(
 1886            editor.selections.display_ranges(cx),
 1887            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1888        );
 1889
 1890        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1891        assert_eq!(
 1892            editor.selections.display_ranges(cx),
 1893            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1894        );
 1895
 1896        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1897        assert_eq!(
 1898            editor.selections.display_ranges(cx),
 1899            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1900        );
 1901    });
 1902}
 1903
 1904#[gpui::test]
 1905async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 1906    init_test(cx, |_| {});
 1907    let mut cx = EditorTestContext::new(cx).await;
 1908
 1909    let line_height = cx.editor(|editor, window, _| {
 1910        editor
 1911            .style()
 1912            .unwrap()
 1913            .text
 1914            .line_height_in_pixels(window.rem_size())
 1915    });
 1916    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1917
 1918    cx.set_state(
 1919        &r#"ˇone
 1920        two
 1921
 1922        three
 1923        fourˇ
 1924        five
 1925
 1926        six"#
 1927            .unindent(),
 1928    );
 1929
 1930    cx.update_editor(|editor, window, cx| {
 1931        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1932    });
 1933    cx.assert_editor_state(
 1934        &r#"one
 1935        two
 1936        ˇ
 1937        three
 1938        four
 1939        five
 1940        ˇ
 1941        six"#
 1942            .unindent(),
 1943    );
 1944
 1945    cx.update_editor(|editor, window, cx| {
 1946        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1947    });
 1948    cx.assert_editor_state(
 1949        &r#"one
 1950        two
 1951
 1952        three
 1953        four
 1954        five
 1955        ˇ
 1956        sixˇ"#
 1957            .unindent(),
 1958    );
 1959
 1960    cx.update_editor(|editor, window, cx| {
 1961        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1962    });
 1963    cx.assert_editor_state(
 1964        &r#"one
 1965        two
 1966
 1967        three
 1968        four
 1969        five
 1970
 1971        sixˇ"#
 1972            .unindent(),
 1973    );
 1974
 1975    cx.update_editor(|editor, window, cx| {
 1976        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1977    });
 1978    cx.assert_editor_state(
 1979        &r#"one
 1980        two
 1981
 1982        three
 1983        four
 1984        five
 1985        ˇ
 1986        six"#
 1987            .unindent(),
 1988    );
 1989
 1990    cx.update_editor(|editor, window, cx| {
 1991        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1992    });
 1993    cx.assert_editor_state(
 1994        &r#"one
 1995        two
 1996        ˇ
 1997        three
 1998        four
 1999        five
 2000
 2001        six"#
 2002            .unindent(),
 2003    );
 2004
 2005    cx.update_editor(|editor, window, cx| {
 2006        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2007    });
 2008    cx.assert_editor_state(
 2009        &r#"ˇone
 2010        two
 2011
 2012        three
 2013        four
 2014        five
 2015
 2016        six"#
 2017            .unindent(),
 2018    );
 2019}
 2020
 2021#[gpui::test]
 2022async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2023    init_test(cx, |_| {});
 2024    let mut cx = EditorTestContext::new(cx).await;
 2025    let line_height = cx.editor(|editor, window, _| {
 2026        editor
 2027            .style()
 2028            .unwrap()
 2029            .text
 2030            .line_height_in_pixels(window.rem_size())
 2031    });
 2032    let window = cx.window;
 2033    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2034
 2035    cx.set_state(
 2036        r#"ˇone
 2037        two
 2038        three
 2039        four
 2040        five
 2041        six
 2042        seven
 2043        eight
 2044        nine
 2045        ten
 2046        "#,
 2047    );
 2048
 2049    cx.update_editor(|editor, window, cx| {
 2050        assert_eq!(
 2051            editor.snapshot(window, cx).scroll_position(),
 2052            gpui::Point::new(0., 0.)
 2053        );
 2054        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2055        assert_eq!(
 2056            editor.snapshot(window, cx).scroll_position(),
 2057            gpui::Point::new(0., 3.)
 2058        );
 2059        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2060        assert_eq!(
 2061            editor.snapshot(window, cx).scroll_position(),
 2062            gpui::Point::new(0., 6.)
 2063        );
 2064        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2065        assert_eq!(
 2066            editor.snapshot(window, cx).scroll_position(),
 2067            gpui::Point::new(0., 3.)
 2068        );
 2069
 2070        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2071        assert_eq!(
 2072            editor.snapshot(window, cx).scroll_position(),
 2073            gpui::Point::new(0., 1.)
 2074        );
 2075        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2076        assert_eq!(
 2077            editor.snapshot(window, cx).scroll_position(),
 2078            gpui::Point::new(0., 3.)
 2079        );
 2080    });
 2081}
 2082
 2083#[gpui::test]
 2084async fn test_autoscroll(cx: &mut TestAppContext) {
 2085    init_test(cx, |_| {});
 2086    let mut cx = EditorTestContext::new(cx).await;
 2087
 2088    let line_height = cx.update_editor(|editor, window, cx| {
 2089        editor.set_vertical_scroll_margin(2, cx);
 2090        editor
 2091            .style()
 2092            .unwrap()
 2093            .text
 2094            .line_height_in_pixels(window.rem_size())
 2095    });
 2096    let window = cx.window;
 2097    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2098
 2099    cx.set_state(
 2100        r#"ˇone
 2101            two
 2102            three
 2103            four
 2104            five
 2105            six
 2106            seven
 2107            eight
 2108            nine
 2109            ten
 2110        "#,
 2111    );
 2112    cx.update_editor(|editor, window, cx| {
 2113        assert_eq!(
 2114            editor.snapshot(window, cx).scroll_position(),
 2115            gpui::Point::new(0., 0.0)
 2116        );
 2117    });
 2118
 2119    // Add a cursor below the visible area. Since both cursors cannot fit
 2120    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2121    // allows the vertical scroll margin below that cursor.
 2122    cx.update_editor(|editor, window, cx| {
 2123        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2124            selections.select_ranges([
 2125                Point::new(0, 0)..Point::new(0, 0),
 2126                Point::new(6, 0)..Point::new(6, 0),
 2127            ]);
 2128        })
 2129    });
 2130    cx.update_editor(|editor, window, cx| {
 2131        assert_eq!(
 2132            editor.snapshot(window, cx).scroll_position(),
 2133            gpui::Point::new(0., 3.0)
 2134        );
 2135    });
 2136
 2137    // Move down. The editor cursor scrolls down to track the newest cursor.
 2138    cx.update_editor(|editor, window, cx| {
 2139        editor.move_down(&Default::default(), window, cx);
 2140    });
 2141    cx.update_editor(|editor, window, cx| {
 2142        assert_eq!(
 2143            editor.snapshot(window, cx).scroll_position(),
 2144            gpui::Point::new(0., 4.0)
 2145        );
 2146    });
 2147
 2148    // Add a cursor above the visible area. Since both cursors fit on screen,
 2149    // the editor scrolls to show both.
 2150    cx.update_editor(|editor, window, cx| {
 2151        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2152            selections.select_ranges([
 2153                Point::new(1, 0)..Point::new(1, 0),
 2154                Point::new(6, 0)..Point::new(6, 0),
 2155            ]);
 2156        })
 2157    });
 2158    cx.update_editor(|editor, window, cx| {
 2159        assert_eq!(
 2160            editor.snapshot(window, cx).scroll_position(),
 2161            gpui::Point::new(0., 1.0)
 2162        );
 2163    });
 2164}
 2165
 2166#[gpui::test]
 2167async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2168    init_test(cx, |_| {});
 2169    let mut cx = EditorTestContext::new(cx).await;
 2170
 2171    let line_height = cx.editor(|editor, window, _cx| {
 2172        editor
 2173            .style()
 2174            .unwrap()
 2175            .text
 2176            .line_height_in_pixels(window.rem_size())
 2177    });
 2178    let window = cx.window;
 2179    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2180    cx.set_state(
 2181        &r#"
 2182        ˇone
 2183        two
 2184        threeˇ
 2185        four
 2186        five
 2187        six
 2188        seven
 2189        eight
 2190        nine
 2191        ten
 2192        "#
 2193        .unindent(),
 2194    );
 2195
 2196    cx.update_editor(|editor, window, cx| {
 2197        editor.move_page_down(&MovePageDown::default(), window, cx)
 2198    });
 2199    cx.assert_editor_state(
 2200        &r#"
 2201        one
 2202        two
 2203        three
 2204        ˇfour
 2205        five
 2206        sixˇ
 2207        seven
 2208        eight
 2209        nine
 2210        ten
 2211        "#
 2212        .unindent(),
 2213    );
 2214
 2215    cx.update_editor(|editor, window, cx| {
 2216        editor.move_page_down(&MovePageDown::default(), window, cx)
 2217    });
 2218    cx.assert_editor_state(
 2219        &r#"
 2220        one
 2221        two
 2222        three
 2223        four
 2224        five
 2225        six
 2226        ˇseven
 2227        eight
 2228        nineˇ
 2229        ten
 2230        "#
 2231        .unindent(),
 2232    );
 2233
 2234    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2235    cx.assert_editor_state(
 2236        &r#"
 2237        one
 2238        two
 2239        three
 2240        ˇfour
 2241        five
 2242        sixˇ
 2243        seven
 2244        eight
 2245        nine
 2246        ten
 2247        "#
 2248        .unindent(),
 2249    );
 2250
 2251    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2252    cx.assert_editor_state(
 2253        &r#"
 2254        ˇone
 2255        two
 2256        threeˇ
 2257        four
 2258        five
 2259        six
 2260        seven
 2261        eight
 2262        nine
 2263        ten
 2264        "#
 2265        .unindent(),
 2266    );
 2267
 2268    // Test select collapsing
 2269    cx.update_editor(|editor, window, cx| {
 2270        editor.move_page_down(&MovePageDown::default(), window, cx);
 2271        editor.move_page_down(&MovePageDown::default(), window, cx);
 2272        editor.move_page_down(&MovePageDown::default(), window, cx);
 2273    });
 2274    cx.assert_editor_state(
 2275        &r#"
 2276        one
 2277        two
 2278        three
 2279        four
 2280        five
 2281        six
 2282        seven
 2283        eight
 2284        nine
 2285        ˇten
 2286        ˇ"#
 2287        .unindent(),
 2288    );
 2289}
 2290
 2291#[gpui::test]
 2292async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2293    init_test(cx, |_| {});
 2294    let mut cx = EditorTestContext::new(cx).await;
 2295    cx.set_state("one «two threeˇ» four");
 2296    cx.update_editor(|editor, window, cx| {
 2297        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2298        assert_eq!(editor.text(cx), " four");
 2299    });
 2300}
 2301
 2302#[gpui::test]
 2303fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2304    init_test(cx, |_| {});
 2305
 2306    let editor = cx.add_window(|window, cx| {
 2307        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2308        build_editor(buffer.clone(), window, cx)
 2309    });
 2310
 2311    _ = editor.update(cx, |editor, window, cx| {
 2312        editor.change_selections(None, window, cx, |s| {
 2313            s.select_display_ranges([
 2314                // an empty selection - the preceding word fragment is deleted
 2315                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2316                // characters selected - they are deleted
 2317                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2318            ])
 2319        });
 2320        editor.delete_to_previous_word_start(
 2321            &DeleteToPreviousWordStart {
 2322                ignore_newlines: false,
 2323            },
 2324            window,
 2325            cx,
 2326        );
 2327        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2328    });
 2329
 2330    _ = editor.update(cx, |editor, window, cx| {
 2331        editor.change_selections(None, window, cx, |s| {
 2332            s.select_display_ranges([
 2333                // an empty selection - the following word fragment is deleted
 2334                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2335                // characters selected - they are deleted
 2336                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2337            ])
 2338        });
 2339        editor.delete_to_next_word_end(
 2340            &DeleteToNextWordEnd {
 2341                ignore_newlines: false,
 2342            },
 2343            window,
 2344            cx,
 2345        );
 2346        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2347    });
 2348}
 2349
 2350#[gpui::test]
 2351fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2352    init_test(cx, |_| {});
 2353
 2354    let editor = cx.add_window(|window, cx| {
 2355        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2356        build_editor(buffer.clone(), window, cx)
 2357    });
 2358    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2359        ignore_newlines: false,
 2360    };
 2361    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2362        ignore_newlines: true,
 2363    };
 2364
 2365    _ = editor.update(cx, |editor, window, cx| {
 2366        editor.change_selections(None, window, cx, |s| {
 2367            s.select_display_ranges([
 2368                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2369            ])
 2370        });
 2371        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2372        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2373        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2374        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2375        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2376        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2377        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2378        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2379        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2380        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2381        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2382        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2383    });
 2384}
 2385
 2386#[gpui::test]
 2387fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2388    init_test(cx, |_| {});
 2389
 2390    let editor = cx.add_window(|window, cx| {
 2391        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2392        build_editor(buffer.clone(), window, cx)
 2393    });
 2394    let del_to_next_word_end = DeleteToNextWordEnd {
 2395        ignore_newlines: false,
 2396    };
 2397    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2398        ignore_newlines: true,
 2399    };
 2400
 2401    _ = editor.update(cx, |editor, window, cx| {
 2402        editor.change_selections(None, window, cx, |s| {
 2403            s.select_display_ranges([
 2404                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2405            ])
 2406        });
 2407        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2408        assert_eq!(
 2409            editor.buffer.read(cx).read(cx).text(),
 2410            "one\n   two\nthree\n   four"
 2411        );
 2412        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2413        assert_eq!(
 2414            editor.buffer.read(cx).read(cx).text(),
 2415            "\n   two\nthree\n   four"
 2416        );
 2417        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2418        assert_eq!(
 2419            editor.buffer.read(cx).read(cx).text(),
 2420            "two\nthree\n   four"
 2421        );
 2422        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2423        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2424        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2425        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2426        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2427        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2428    });
 2429}
 2430
 2431#[gpui::test]
 2432fn test_newline(cx: &mut TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let editor = cx.add_window(|window, cx| {
 2436        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2437        build_editor(buffer.clone(), window, cx)
 2438    });
 2439
 2440    _ = editor.update(cx, |editor, window, cx| {
 2441        editor.change_selections(None, window, cx, |s| {
 2442            s.select_display_ranges([
 2443                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2444                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2445                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2446            ])
 2447        });
 2448
 2449        editor.newline(&Newline, window, cx);
 2450        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2451    });
 2452}
 2453
 2454#[gpui::test]
 2455fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2456    init_test(cx, |_| {});
 2457
 2458    let editor = cx.add_window(|window, cx| {
 2459        let buffer = MultiBuffer::build_simple(
 2460            "
 2461                a
 2462                b(
 2463                    X
 2464                )
 2465                c(
 2466                    X
 2467                )
 2468            "
 2469            .unindent()
 2470            .as_str(),
 2471            cx,
 2472        );
 2473        let mut editor = build_editor(buffer.clone(), window, cx);
 2474        editor.change_selections(None, window, cx, |s| {
 2475            s.select_ranges([
 2476                Point::new(2, 4)..Point::new(2, 5),
 2477                Point::new(5, 4)..Point::new(5, 5),
 2478            ])
 2479        });
 2480        editor
 2481    });
 2482
 2483    _ = editor.update(cx, |editor, window, cx| {
 2484        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2485        editor.buffer.update(cx, |buffer, cx| {
 2486            buffer.edit(
 2487                [
 2488                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2489                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2490                ],
 2491                None,
 2492                cx,
 2493            );
 2494            assert_eq!(
 2495                buffer.read(cx).text(),
 2496                "
 2497                    a
 2498                    b()
 2499                    c()
 2500                "
 2501                .unindent()
 2502            );
 2503        });
 2504        assert_eq!(
 2505            editor.selections.ranges(cx),
 2506            &[
 2507                Point::new(1, 2)..Point::new(1, 2),
 2508                Point::new(2, 2)..Point::new(2, 2),
 2509            ],
 2510        );
 2511
 2512        editor.newline(&Newline, window, cx);
 2513        assert_eq!(
 2514            editor.text(cx),
 2515            "
 2516                a
 2517                b(
 2518                )
 2519                c(
 2520                )
 2521            "
 2522            .unindent()
 2523        );
 2524
 2525        // The selections are moved after the inserted newlines
 2526        assert_eq!(
 2527            editor.selections.ranges(cx),
 2528            &[
 2529                Point::new(2, 0)..Point::new(2, 0),
 2530                Point::new(4, 0)..Point::new(4, 0),
 2531            ],
 2532        );
 2533    });
 2534}
 2535
 2536#[gpui::test]
 2537async fn test_newline_above(cx: &mut TestAppContext) {
 2538    init_test(cx, |settings| {
 2539        settings.defaults.tab_size = NonZeroU32::new(4)
 2540    });
 2541
 2542    let language = Arc::new(
 2543        Language::new(
 2544            LanguageConfig::default(),
 2545            Some(tree_sitter_rust::LANGUAGE.into()),
 2546        )
 2547        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2548        .unwrap(),
 2549    );
 2550
 2551    let mut cx = EditorTestContext::new(cx).await;
 2552    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2553    cx.set_state(indoc! {"
 2554        const a: ˇA = (
 2555 2556                «const_functionˇ»(ˇ),
 2557                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2558 2559        ˇ);ˇ
 2560    "});
 2561
 2562    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2563    cx.assert_editor_state(indoc! {"
 2564        ˇ
 2565        const a: A = (
 2566            ˇ
 2567            (
 2568                ˇ
 2569                ˇ
 2570                const_function(),
 2571                ˇ
 2572                ˇ
 2573                ˇ
 2574                ˇ
 2575                something_else,
 2576                ˇ
 2577            )
 2578            ˇ
 2579            ˇ
 2580        );
 2581    "});
 2582}
 2583
 2584#[gpui::test]
 2585async fn test_newline_below(cx: &mut TestAppContext) {
 2586    init_test(cx, |settings| {
 2587        settings.defaults.tab_size = NonZeroU32::new(4)
 2588    });
 2589
 2590    let language = Arc::new(
 2591        Language::new(
 2592            LanguageConfig::default(),
 2593            Some(tree_sitter_rust::LANGUAGE.into()),
 2594        )
 2595        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2596        .unwrap(),
 2597    );
 2598
 2599    let mut cx = EditorTestContext::new(cx).await;
 2600    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2601    cx.set_state(indoc! {"
 2602        const a: ˇA = (
 2603 2604                «const_functionˇ»(ˇ),
 2605                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2606 2607        ˇ);ˇ
 2608    "});
 2609
 2610    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2611    cx.assert_editor_state(indoc! {"
 2612        const a: A = (
 2613            ˇ
 2614            (
 2615                ˇ
 2616                const_function(),
 2617                ˇ
 2618                ˇ
 2619                something_else,
 2620                ˇ
 2621                ˇ
 2622                ˇ
 2623                ˇ
 2624            )
 2625            ˇ
 2626        );
 2627        ˇ
 2628        ˇ
 2629    "});
 2630}
 2631
 2632#[gpui::test]
 2633async fn test_newline_comments(cx: &mut TestAppContext) {
 2634    init_test(cx, |settings| {
 2635        settings.defaults.tab_size = NonZeroU32::new(4)
 2636    });
 2637
 2638    let language = Arc::new(Language::new(
 2639        LanguageConfig {
 2640            line_comments: vec!["//".into()],
 2641            ..LanguageConfig::default()
 2642        },
 2643        None,
 2644    ));
 2645    {
 2646        let mut cx = EditorTestContext::new(cx).await;
 2647        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2648        cx.set_state(indoc! {"
 2649        // Fooˇ
 2650    "});
 2651
 2652        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2653        cx.assert_editor_state(indoc! {"
 2654        // Foo
 2655        //ˇ
 2656    "});
 2657        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2658        cx.set_state(indoc! {"
 2659        ˇ// Foo
 2660    "});
 2661        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2662        cx.assert_editor_state(indoc! {"
 2663
 2664        ˇ// Foo
 2665    "});
 2666    }
 2667    // Ensure that comment continuations can be disabled.
 2668    update_test_language_settings(cx, |settings| {
 2669        settings.defaults.extend_comment_on_newline = Some(false);
 2670    });
 2671    let mut cx = EditorTestContext::new(cx).await;
 2672    cx.set_state(indoc! {"
 2673        // Fooˇ
 2674    "});
 2675    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        // Foo
 2678        ˇ
 2679    "});
 2680}
 2681
 2682#[gpui::test]
 2683fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2684    init_test(cx, |_| {});
 2685
 2686    let editor = cx.add_window(|window, cx| {
 2687        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2688        let mut editor = build_editor(buffer.clone(), window, cx);
 2689        editor.change_selections(None, window, cx, |s| {
 2690            s.select_ranges([3..4, 11..12, 19..20])
 2691        });
 2692        editor
 2693    });
 2694
 2695    _ = editor.update(cx, |editor, window, cx| {
 2696        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2697        editor.buffer.update(cx, |buffer, cx| {
 2698            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2699            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2700        });
 2701        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2702
 2703        editor.insert("Z", window, cx);
 2704        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2705
 2706        // The selections are moved after the inserted characters
 2707        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2708    });
 2709}
 2710
 2711#[gpui::test]
 2712async fn test_tab(cx: &mut TestAppContext) {
 2713    init_test(cx, |settings| {
 2714        settings.defaults.tab_size = NonZeroU32::new(3)
 2715    });
 2716
 2717    let mut cx = EditorTestContext::new(cx).await;
 2718    cx.set_state(indoc! {"
 2719        ˇabˇc
 2720        ˇ🏀ˇ🏀ˇefg
 2721 2722    "});
 2723    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2724    cx.assert_editor_state(indoc! {"
 2725           ˇab ˇc
 2726           ˇ🏀  ˇ🏀  ˇefg
 2727        d  ˇ
 2728    "});
 2729
 2730    cx.set_state(indoc! {"
 2731        a
 2732        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2733    "});
 2734    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2735    cx.assert_editor_state(indoc! {"
 2736        a
 2737           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2738    "});
 2739}
 2740
 2741#[gpui::test]
 2742async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2743    init_test(cx, |_| {});
 2744
 2745    let mut cx = EditorTestContext::new(cx).await;
 2746    let language = Arc::new(
 2747        Language::new(
 2748            LanguageConfig::default(),
 2749            Some(tree_sitter_rust::LANGUAGE.into()),
 2750        )
 2751        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2752        .unwrap(),
 2753    );
 2754    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2755
 2756    // cursors that are already at the suggested indent level insert
 2757    // a soft tab. cursors that are to the left of the suggested indent
 2758    // auto-indent their line.
 2759    cx.set_state(indoc! {"
 2760        ˇ
 2761        const a: B = (
 2762            c(
 2763                d(
 2764        ˇ
 2765                )
 2766        ˇ
 2767        ˇ    )
 2768        );
 2769    "});
 2770    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2771    cx.assert_editor_state(indoc! {"
 2772            ˇ
 2773        const a: B = (
 2774            c(
 2775                d(
 2776                    ˇ
 2777                )
 2778                ˇ
 2779            ˇ)
 2780        );
 2781    "});
 2782
 2783    // handle auto-indent when there are multiple cursors on the same line
 2784    cx.set_state(indoc! {"
 2785        const a: B = (
 2786            c(
 2787        ˇ    ˇ
 2788        ˇ    )
 2789        );
 2790    "});
 2791    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2792    cx.assert_editor_state(indoc! {"
 2793        const a: B = (
 2794            c(
 2795                ˇ
 2796            ˇ)
 2797        );
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2803    init_test(cx, |settings| {
 2804        settings.defaults.tab_size = NonZeroU32::new(4)
 2805    });
 2806
 2807    let language = Arc::new(
 2808        Language::new(
 2809            LanguageConfig::default(),
 2810            Some(tree_sitter_rust::LANGUAGE.into()),
 2811        )
 2812        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2813        .unwrap(),
 2814    );
 2815
 2816    let mut cx = EditorTestContext::new(cx).await;
 2817    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2818    cx.set_state(indoc! {"
 2819        fn a() {
 2820            if b {
 2821        \t ˇc
 2822            }
 2823        }
 2824    "});
 2825
 2826    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2827    cx.assert_editor_state(indoc! {"
 2828        fn a() {
 2829            if b {
 2830                ˇc
 2831            }
 2832        }
 2833    "});
 2834}
 2835
 2836#[gpui::test]
 2837async fn test_indent_outdent(cx: &mut TestAppContext) {
 2838    init_test(cx, |settings| {
 2839        settings.defaults.tab_size = NonZeroU32::new(4);
 2840    });
 2841
 2842    let mut cx = EditorTestContext::new(cx).await;
 2843
 2844    cx.set_state(indoc! {"
 2845          «oneˇ» «twoˇ»
 2846        three
 2847         four
 2848    "});
 2849    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2850    cx.assert_editor_state(indoc! {"
 2851            «oneˇ» «twoˇ»
 2852        three
 2853         four
 2854    "});
 2855
 2856    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2857    cx.assert_editor_state(indoc! {"
 2858        «oneˇ» «twoˇ»
 2859        three
 2860         four
 2861    "});
 2862
 2863    // select across line ending
 2864    cx.set_state(indoc! {"
 2865        one two
 2866        t«hree
 2867        ˇ» four
 2868    "});
 2869    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2870    cx.assert_editor_state(indoc! {"
 2871        one two
 2872            t«hree
 2873        ˇ» four
 2874    "});
 2875
 2876    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2877    cx.assert_editor_state(indoc! {"
 2878        one two
 2879        t«hree
 2880        ˇ» four
 2881    "});
 2882
 2883    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2884    cx.set_state(indoc! {"
 2885        one two
 2886        ˇthree
 2887            four
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891        one two
 2892            ˇthree
 2893            four
 2894    "});
 2895
 2896    cx.set_state(indoc! {"
 2897        one two
 2898        ˇ    three
 2899            four
 2900    "});
 2901    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2902    cx.assert_editor_state(indoc! {"
 2903        one two
 2904        ˇthree
 2905            four
 2906    "});
 2907}
 2908
 2909#[gpui::test]
 2910async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 2911    init_test(cx, |settings| {
 2912        settings.defaults.hard_tabs = Some(true);
 2913    });
 2914
 2915    let mut cx = EditorTestContext::new(cx).await;
 2916
 2917    // select two ranges on one line
 2918    cx.set_state(indoc! {"
 2919        «oneˇ» «twoˇ»
 2920        three
 2921        four
 2922    "});
 2923    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2924    cx.assert_editor_state(indoc! {"
 2925        \t«oneˇ» «twoˇ»
 2926        three
 2927        four
 2928    "});
 2929    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2930    cx.assert_editor_state(indoc! {"
 2931        \t\t«oneˇ» «twoˇ»
 2932        three
 2933        four
 2934    "});
 2935    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2936    cx.assert_editor_state(indoc! {"
 2937        \t«oneˇ» «twoˇ»
 2938        three
 2939        four
 2940    "});
 2941    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2942    cx.assert_editor_state(indoc! {"
 2943        «oneˇ» «twoˇ»
 2944        three
 2945        four
 2946    "});
 2947
 2948    // select across a line ending
 2949    cx.set_state(indoc! {"
 2950        one two
 2951        t«hree
 2952        ˇ»four
 2953    "});
 2954    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2955    cx.assert_editor_state(indoc! {"
 2956        one two
 2957        \tt«hree
 2958        ˇ»four
 2959    "});
 2960    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2961    cx.assert_editor_state(indoc! {"
 2962        one two
 2963        \t\tt«hree
 2964        ˇ»four
 2965    "});
 2966    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2967    cx.assert_editor_state(indoc! {"
 2968        one two
 2969        \tt«hree
 2970        ˇ»four
 2971    "});
 2972    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2973    cx.assert_editor_state(indoc! {"
 2974        one two
 2975        t«hree
 2976        ˇ»four
 2977    "});
 2978
 2979    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2980    cx.set_state(indoc! {"
 2981        one two
 2982        ˇthree
 2983        four
 2984    "});
 2985    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2986    cx.assert_editor_state(indoc! {"
 2987        one two
 2988        ˇthree
 2989        four
 2990    "});
 2991    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2992    cx.assert_editor_state(indoc! {"
 2993        one two
 2994        \tˇthree
 2995        four
 2996    "});
 2997    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2998    cx.assert_editor_state(indoc! {"
 2999        one two
 3000        ˇthree
 3001        four
 3002    "});
 3003}
 3004
 3005#[gpui::test]
 3006fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3007    init_test(cx, |settings| {
 3008        settings.languages.extend([
 3009            (
 3010                "TOML".into(),
 3011                LanguageSettingsContent {
 3012                    tab_size: NonZeroU32::new(2),
 3013                    ..Default::default()
 3014                },
 3015            ),
 3016            (
 3017                "Rust".into(),
 3018                LanguageSettingsContent {
 3019                    tab_size: NonZeroU32::new(4),
 3020                    ..Default::default()
 3021                },
 3022            ),
 3023        ]);
 3024    });
 3025
 3026    let toml_language = Arc::new(Language::new(
 3027        LanguageConfig {
 3028            name: "TOML".into(),
 3029            ..Default::default()
 3030        },
 3031        None,
 3032    ));
 3033    let rust_language = Arc::new(Language::new(
 3034        LanguageConfig {
 3035            name: "Rust".into(),
 3036            ..Default::default()
 3037        },
 3038        None,
 3039    ));
 3040
 3041    let toml_buffer =
 3042        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3043    let rust_buffer =
 3044        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3045    let multibuffer = cx.new(|cx| {
 3046        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3047        multibuffer.push_excerpts(
 3048            toml_buffer.clone(),
 3049            [ExcerptRange {
 3050                context: Point::new(0, 0)..Point::new(2, 0),
 3051                primary: None,
 3052            }],
 3053            cx,
 3054        );
 3055        multibuffer.push_excerpts(
 3056            rust_buffer.clone(),
 3057            [ExcerptRange {
 3058                context: Point::new(0, 0)..Point::new(1, 0),
 3059                primary: None,
 3060            }],
 3061            cx,
 3062        );
 3063        multibuffer
 3064    });
 3065
 3066    cx.add_window(|window, cx| {
 3067        let mut editor = build_editor(multibuffer, window, cx);
 3068
 3069        assert_eq!(
 3070            editor.text(cx),
 3071            indoc! {"
 3072                a = 1
 3073                b = 2
 3074
 3075                const c: usize = 3;
 3076            "}
 3077        );
 3078
 3079        select_ranges(
 3080            &mut editor,
 3081            indoc! {"
 3082                «aˇ» = 1
 3083                b = 2
 3084
 3085                «const c:ˇ» usize = 3;
 3086            "},
 3087            window,
 3088            cx,
 3089        );
 3090
 3091        editor.tab(&Tab, window, cx);
 3092        assert_text_with_selections(
 3093            &mut editor,
 3094            indoc! {"
 3095                  «aˇ» = 1
 3096                b = 2
 3097
 3098                    «const c:ˇ» usize = 3;
 3099            "},
 3100            cx,
 3101        );
 3102        editor.tab_prev(&TabPrev, window, cx);
 3103        assert_text_with_selections(
 3104            &mut editor,
 3105            indoc! {"
 3106                «aˇ» = 1
 3107                b = 2
 3108
 3109                «const c:ˇ» usize = 3;
 3110            "},
 3111            cx,
 3112        );
 3113
 3114        editor
 3115    });
 3116}
 3117
 3118#[gpui::test]
 3119async fn test_backspace(cx: &mut TestAppContext) {
 3120    init_test(cx, |_| {});
 3121
 3122    let mut cx = EditorTestContext::new(cx).await;
 3123
 3124    // Basic backspace
 3125    cx.set_state(indoc! {"
 3126        onˇe two three
 3127        fou«rˇ» five six
 3128        seven «ˇeight nine
 3129        »ten
 3130    "});
 3131    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3132    cx.assert_editor_state(indoc! {"
 3133        oˇe two three
 3134        fouˇ five six
 3135        seven ˇten
 3136    "});
 3137
 3138    // Test backspace inside and around indents
 3139    cx.set_state(indoc! {"
 3140        zero
 3141            ˇone
 3142                ˇtwo
 3143            ˇ ˇ ˇ  three
 3144        ˇ  ˇ  four
 3145    "});
 3146    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3147    cx.assert_editor_state(indoc! {"
 3148        zero
 3149        ˇone
 3150            ˇtwo
 3151        ˇ  threeˇ  four
 3152    "});
 3153
 3154    // Test backspace with line_mode set to true
 3155    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3156    cx.set_state(indoc! {"
 3157        The ˇquick ˇbrown
 3158        fox jumps over
 3159        the lazy dog
 3160        ˇThe qu«ick bˇ»rown"});
 3161    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3162    cx.assert_editor_state(indoc! {"
 3163        ˇfox jumps over
 3164        the lazy dogˇ"});
 3165}
 3166
 3167#[gpui::test]
 3168async fn test_delete(cx: &mut TestAppContext) {
 3169    init_test(cx, |_| {});
 3170
 3171    let mut cx = EditorTestContext::new(cx).await;
 3172    cx.set_state(indoc! {"
 3173        onˇe two three
 3174        fou«rˇ» five six
 3175        seven «ˇeight nine
 3176        »ten
 3177    "});
 3178    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3179    cx.assert_editor_state(indoc! {"
 3180        onˇ two three
 3181        fouˇ five six
 3182        seven ˇten
 3183    "});
 3184
 3185    // Test backspace with line_mode set to true
 3186    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3187    cx.set_state(indoc! {"
 3188        The ˇquick ˇbrown
 3189        fox «ˇjum»ps over
 3190        the lazy dog
 3191        ˇThe qu«ick bˇ»rown"});
 3192    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3193    cx.assert_editor_state("ˇthe lazy dogˇ");
 3194}
 3195
 3196#[gpui::test]
 3197fn test_delete_line(cx: &mut TestAppContext) {
 3198    init_test(cx, |_| {});
 3199
 3200    let editor = cx.add_window(|window, cx| {
 3201        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3202        build_editor(buffer, window, cx)
 3203    });
 3204    _ = editor.update(cx, |editor, window, cx| {
 3205        editor.change_selections(None, window, cx, |s| {
 3206            s.select_display_ranges([
 3207                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3208                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3209                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3210            ])
 3211        });
 3212        editor.delete_line(&DeleteLine, window, cx);
 3213        assert_eq!(editor.display_text(cx), "ghi");
 3214        assert_eq!(
 3215            editor.selections.display_ranges(cx),
 3216            vec![
 3217                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3218                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3219            ]
 3220        );
 3221    });
 3222
 3223    let editor = cx.add_window(|window, cx| {
 3224        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3225        build_editor(buffer, window, cx)
 3226    });
 3227    _ = editor.update(cx, |editor, window, cx| {
 3228        editor.change_selections(None, window, cx, |s| {
 3229            s.select_display_ranges([
 3230                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3231            ])
 3232        });
 3233        editor.delete_line(&DeleteLine, window, cx);
 3234        assert_eq!(editor.display_text(cx), "ghi\n");
 3235        assert_eq!(
 3236            editor.selections.display_ranges(cx),
 3237            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3238        );
 3239    });
 3240}
 3241
 3242#[gpui::test]
 3243fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3244    init_test(cx, |_| {});
 3245
 3246    cx.add_window(|window, cx| {
 3247        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3248        let mut editor = build_editor(buffer.clone(), window, cx);
 3249        let buffer = buffer.read(cx).as_singleton().unwrap();
 3250
 3251        assert_eq!(
 3252            editor.selections.ranges::<Point>(cx),
 3253            &[Point::new(0, 0)..Point::new(0, 0)]
 3254        );
 3255
 3256        // When on single line, replace newline at end by space
 3257        editor.join_lines(&JoinLines, window, cx);
 3258        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3259        assert_eq!(
 3260            editor.selections.ranges::<Point>(cx),
 3261            &[Point::new(0, 3)..Point::new(0, 3)]
 3262        );
 3263
 3264        // When multiple lines are selected, remove newlines that are spanned by the selection
 3265        editor.change_selections(None, window, cx, |s| {
 3266            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3267        });
 3268        editor.join_lines(&JoinLines, window, cx);
 3269        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3270        assert_eq!(
 3271            editor.selections.ranges::<Point>(cx),
 3272            &[Point::new(0, 11)..Point::new(0, 11)]
 3273        );
 3274
 3275        // Undo should be transactional
 3276        editor.undo(&Undo, window, cx);
 3277        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3278        assert_eq!(
 3279            editor.selections.ranges::<Point>(cx),
 3280            &[Point::new(0, 5)..Point::new(2, 2)]
 3281        );
 3282
 3283        // When joining an empty line don't insert a space
 3284        editor.change_selections(None, window, cx, |s| {
 3285            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3286        });
 3287        editor.join_lines(&JoinLines, window, cx);
 3288        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3289        assert_eq!(
 3290            editor.selections.ranges::<Point>(cx),
 3291            [Point::new(2, 3)..Point::new(2, 3)]
 3292        );
 3293
 3294        // We can remove trailing newlines
 3295        editor.join_lines(&JoinLines, window, cx);
 3296        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3297        assert_eq!(
 3298            editor.selections.ranges::<Point>(cx),
 3299            [Point::new(2, 3)..Point::new(2, 3)]
 3300        );
 3301
 3302        // We don't blow up on the last line
 3303        editor.join_lines(&JoinLines, window, cx);
 3304        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3305        assert_eq!(
 3306            editor.selections.ranges::<Point>(cx),
 3307            [Point::new(2, 3)..Point::new(2, 3)]
 3308        );
 3309
 3310        // reset to test indentation
 3311        editor.buffer.update(cx, |buffer, cx| {
 3312            buffer.edit(
 3313                [
 3314                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3315                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3316                ],
 3317                None,
 3318                cx,
 3319            )
 3320        });
 3321
 3322        // We remove any leading spaces
 3323        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3324        editor.change_selections(None, window, cx, |s| {
 3325            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3326        });
 3327        editor.join_lines(&JoinLines, window, cx);
 3328        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3329
 3330        // We don't insert a space for a line containing only spaces
 3331        editor.join_lines(&JoinLines, window, cx);
 3332        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3333
 3334        // We ignore any leading tabs
 3335        editor.join_lines(&JoinLines, window, cx);
 3336        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3337
 3338        editor
 3339    });
 3340}
 3341
 3342#[gpui::test]
 3343fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3344    init_test(cx, |_| {});
 3345
 3346    cx.add_window(|window, cx| {
 3347        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3348        let mut editor = build_editor(buffer.clone(), window, cx);
 3349        let buffer = buffer.read(cx).as_singleton().unwrap();
 3350
 3351        editor.change_selections(None, window, cx, |s| {
 3352            s.select_ranges([
 3353                Point::new(0, 2)..Point::new(1, 1),
 3354                Point::new(1, 2)..Point::new(1, 2),
 3355                Point::new(3, 1)..Point::new(3, 2),
 3356            ])
 3357        });
 3358
 3359        editor.join_lines(&JoinLines, window, cx);
 3360        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3361
 3362        assert_eq!(
 3363            editor.selections.ranges::<Point>(cx),
 3364            [
 3365                Point::new(0, 7)..Point::new(0, 7),
 3366                Point::new(1, 3)..Point::new(1, 3)
 3367            ]
 3368        );
 3369        editor
 3370    });
 3371}
 3372
 3373#[gpui::test]
 3374async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3375    init_test(cx, |_| {});
 3376
 3377    let mut cx = EditorTestContext::new(cx).await;
 3378
 3379    let diff_base = r#"
 3380        Line 0
 3381        Line 1
 3382        Line 2
 3383        Line 3
 3384        "#
 3385    .unindent();
 3386
 3387    cx.set_state(
 3388        &r#"
 3389        ˇLine 0
 3390        Line 1
 3391        Line 2
 3392        Line 3
 3393        "#
 3394        .unindent(),
 3395    );
 3396
 3397    cx.set_head_text(&diff_base);
 3398    executor.run_until_parked();
 3399
 3400    // Join lines
 3401    cx.update_editor(|editor, window, cx| {
 3402        editor.join_lines(&JoinLines, window, cx);
 3403    });
 3404    executor.run_until_parked();
 3405
 3406    cx.assert_editor_state(
 3407        &r#"
 3408        Line 0ˇ Line 1
 3409        Line 2
 3410        Line 3
 3411        "#
 3412        .unindent(),
 3413    );
 3414    // Join again
 3415    cx.update_editor(|editor, window, cx| {
 3416        editor.join_lines(&JoinLines, window, cx);
 3417    });
 3418    executor.run_until_parked();
 3419
 3420    cx.assert_editor_state(
 3421        &r#"
 3422        Line 0 Line 1ˇ Line 2
 3423        Line 3
 3424        "#
 3425        .unindent(),
 3426    );
 3427}
 3428
 3429#[gpui::test]
 3430async fn test_custom_newlines_cause_no_false_positive_diffs(
 3431    executor: BackgroundExecutor,
 3432    cx: &mut TestAppContext,
 3433) {
 3434    init_test(cx, |_| {});
 3435    let mut cx = EditorTestContext::new(cx).await;
 3436    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3437    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3438    executor.run_until_parked();
 3439
 3440    cx.update_editor(|editor, window, cx| {
 3441        let snapshot = editor.snapshot(window, cx);
 3442        assert_eq!(
 3443            snapshot
 3444                .buffer_snapshot
 3445                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3446                .collect::<Vec<_>>(),
 3447            Vec::new(),
 3448            "Should not have any diffs for files with custom newlines"
 3449        );
 3450    });
 3451}
 3452
 3453#[gpui::test]
 3454async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3455    init_test(cx, |_| {});
 3456
 3457    let mut cx = EditorTestContext::new(cx).await;
 3458
 3459    // Test sort_lines_case_insensitive()
 3460    cx.set_state(indoc! {"
 3461        «z
 3462        y
 3463        x
 3464        Z
 3465        Y
 3466        Xˇ»
 3467    "});
 3468    cx.update_editor(|e, window, cx| {
 3469        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3470    });
 3471    cx.assert_editor_state(indoc! {"
 3472        «x
 3473        X
 3474        y
 3475        Y
 3476        z
 3477        Zˇ»
 3478    "});
 3479
 3480    // Test reverse_lines()
 3481    cx.set_state(indoc! {"
 3482        «5
 3483        4
 3484        3
 3485        2
 3486        1ˇ»
 3487    "});
 3488    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3489    cx.assert_editor_state(indoc! {"
 3490        «1
 3491        2
 3492        3
 3493        4
 3494        5ˇ»
 3495    "});
 3496
 3497    // Skip testing shuffle_line()
 3498
 3499    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3500    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3501
 3502    // Don't manipulate when cursor is on single line, but expand the selection
 3503    cx.set_state(indoc! {"
 3504        ddˇdd
 3505        ccc
 3506        bb
 3507        a
 3508    "});
 3509    cx.update_editor(|e, window, cx| {
 3510        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3511    });
 3512    cx.assert_editor_state(indoc! {"
 3513        «ddddˇ»
 3514        ccc
 3515        bb
 3516        a
 3517    "});
 3518
 3519    // Basic manipulate case
 3520    // Start selection moves to column 0
 3521    // End of selection shrinks to fit shorter line
 3522    cx.set_state(indoc! {"
 3523        dd«d
 3524        ccc
 3525        bb
 3526        aaaaaˇ»
 3527    "});
 3528    cx.update_editor(|e, window, cx| {
 3529        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3530    });
 3531    cx.assert_editor_state(indoc! {"
 3532        «aaaaa
 3533        bb
 3534        ccc
 3535        dddˇ»
 3536    "});
 3537
 3538    // Manipulate case with newlines
 3539    cx.set_state(indoc! {"
 3540        dd«d
 3541        ccc
 3542
 3543        bb
 3544        aaaaa
 3545
 3546        ˇ»
 3547    "});
 3548    cx.update_editor(|e, window, cx| {
 3549        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3550    });
 3551    cx.assert_editor_state(indoc! {"
 3552        «
 3553
 3554        aaaaa
 3555        bb
 3556        ccc
 3557        dddˇ»
 3558
 3559    "});
 3560
 3561    // Adding new line
 3562    cx.set_state(indoc! {"
 3563        aa«a
 3564        bbˇ»b
 3565    "});
 3566    cx.update_editor(|e, window, cx| {
 3567        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3568    });
 3569    cx.assert_editor_state(indoc! {"
 3570        «aaa
 3571        bbb
 3572        added_lineˇ»
 3573    "});
 3574
 3575    // Removing line
 3576    cx.set_state(indoc! {"
 3577        aa«a
 3578        bbbˇ»
 3579    "});
 3580    cx.update_editor(|e, window, cx| {
 3581        e.manipulate_lines(window, cx, |lines| {
 3582            lines.pop();
 3583        })
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «aaaˇ»
 3587    "});
 3588
 3589    // Removing all lines
 3590    cx.set_state(indoc! {"
 3591        aa«a
 3592        bbbˇ»
 3593    "});
 3594    cx.update_editor(|e, window, cx| {
 3595        e.manipulate_lines(window, cx, |lines| {
 3596            lines.drain(..);
 3597        })
 3598    });
 3599    cx.assert_editor_state(indoc! {"
 3600        ˇ
 3601    "});
 3602}
 3603
 3604#[gpui::test]
 3605async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3606    init_test(cx, |_| {});
 3607
 3608    let mut cx = EditorTestContext::new(cx).await;
 3609
 3610    // Consider continuous selection as single selection
 3611    cx.set_state(indoc! {"
 3612        Aaa«aa
 3613        cˇ»c«c
 3614        bb
 3615        aaaˇ»aa
 3616    "});
 3617    cx.update_editor(|e, window, cx| {
 3618        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3619    });
 3620    cx.assert_editor_state(indoc! {"
 3621        «Aaaaa
 3622        ccc
 3623        bb
 3624        aaaaaˇ»
 3625    "});
 3626
 3627    cx.set_state(indoc! {"
 3628        Aaa«aa
 3629        cˇ»c«c
 3630        bb
 3631        aaaˇ»aa
 3632    "});
 3633    cx.update_editor(|e, window, cx| {
 3634        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3635    });
 3636    cx.assert_editor_state(indoc! {"
 3637        «Aaaaa
 3638        ccc
 3639        bbˇ»
 3640    "});
 3641
 3642    // Consider non continuous selection as distinct dedup operations
 3643    cx.set_state(indoc! {"
 3644        «aaaaa
 3645        bb
 3646        aaaaa
 3647        aaaaaˇ»
 3648
 3649        aaa«aaˇ»
 3650    "});
 3651    cx.update_editor(|e, window, cx| {
 3652        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3653    });
 3654    cx.assert_editor_state(indoc! {"
 3655        «aaaaa
 3656        bbˇ»
 3657
 3658        «aaaaaˇ»
 3659    "});
 3660}
 3661
 3662#[gpui::test]
 3663async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3664    init_test(cx, |_| {});
 3665
 3666    let mut cx = EditorTestContext::new(cx).await;
 3667
 3668    cx.set_state(indoc! {"
 3669        «Aaa
 3670        aAa
 3671        Aaaˇ»
 3672    "});
 3673    cx.update_editor(|e, window, cx| {
 3674        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «Aaa
 3678        aAaˇ»
 3679    "});
 3680
 3681    cx.set_state(indoc! {"
 3682        «Aaa
 3683        aAa
 3684        aaAˇ»
 3685    "});
 3686    cx.update_editor(|e, window, cx| {
 3687        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3688    });
 3689    cx.assert_editor_state(indoc! {"
 3690        «Aaaˇ»
 3691    "});
 3692}
 3693
 3694#[gpui::test]
 3695async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3696    init_test(cx, |_| {});
 3697
 3698    let mut cx = EditorTestContext::new(cx).await;
 3699
 3700    // Manipulate with multiple selections on a single line
 3701    cx.set_state(indoc! {"
 3702        dd«dd
 3703        cˇ»c«c
 3704        bb
 3705        aaaˇ»aa
 3706    "});
 3707    cx.update_editor(|e, window, cx| {
 3708        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3709    });
 3710    cx.assert_editor_state(indoc! {"
 3711        «aaaaa
 3712        bb
 3713        ccc
 3714        ddddˇ»
 3715    "});
 3716
 3717    // Manipulate with multiple disjoin selections
 3718    cx.set_state(indoc! {"
 3719 3720        4
 3721        3
 3722        2
 3723        1ˇ»
 3724
 3725        dd«dd
 3726        ccc
 3727        bb
 3728        aaaˇ»aa
 3729    "});
 3730    cx.update_editor(|e, window, cx| {
 3731        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3732    });
 3733    cx.assert_editor_state(indoc! {"
 3734        «1
 3735        2
 3736        3
 3737        4
 3738        5ˇ»
 3739
 3740        «aaaaa
 3741        bb
 3742        ccc
 3743        ddddˇ»
 3744    "});
 3745
 3746    // Adding lines on each selection
 3747    cx.set_state(indoc! {"
 3748 3749        1ˇ»
 3750
 3751        bb«bb
 3752        aaaˇ»aa
 3753    "});
 3754    cx.update_editor(|e, window, cx| {
 3755        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3756    });
 3757    cx.assert_editor_state(indoc! {"
 3758        «2
 3759        1
 3760        added lineˇ»
 3761
 3762        «bbbb
 3763        aaaaa
 3764        added lineˇ»
 3765    "});
 3766
 3767    // Removing lines on each selection
 3768    cx.set_state(indoc! {"
 3769 3770        1ˇ»
 3771
 3772        bb«bb
 3773        aaaˇ»aa
 3774    "});
 3775    cx.update_editor(|e, window, cx| {
 3776        e.manipulate_lines(window, cx, |lines| {
 3777            lines.pop();
 3778        })
 3779    });
 3780    cx.assert_editor_state(indoc! {"
 3781        «2ˇ»
 3782
 3783        «bbbbˇ»
 3784    "});
 3785}
 3786
 3787#[gpui::test]
 3788async fn test_manipulate_text(cx: &mut TestAppContext) {
 3789    init_test(cx, |_| {});
 3790
 3791    let mut cx = EditorTestContext::new(cx).await;
 3792
 3793    // Test convert_to_upper_case()
 3794    cx.set_state(indoc! {"
 3795        «hello worldˇ»
 3796    "});
 3797    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3798    cx.assert_editor_state(indoc! {"
 3799        «HELLO WORLDˇ»
 3800    "});
 3801
 3802    // Test convert_to_lower_case()
 3803    cx.set_state(indoc! {"
 3804        «HELLO WORLDˇ»
 3805    "});
 3806    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3807    cx.assert_editor_state(indoc! {"
 3808        «hello worldˇ»
 3809    "});
 3810
 3811    // Test multiple line, single selection case
 3812    cx.set_state(indoc! {"
 3813        «The quick brown
 3814        fox jumps over
 3815        the lazy dogˇ»
 3816    "});
 3817    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3818    cx.assert_editor_state(indoc! {"
 3819        «The Quick Brown
 3820        Fox Jumps Over
 3821        The Lazy Dogˇ»
 3822    "});
 3823
 3824    // Test multiple line, single selection case
 3825    cx.set_state(indoc! {"
 3826        «The quick brown
 3827        fox jumps over
 3828        the lazy dogˇ»
 3829    "});
 3830    cx.update_editor(|e, window, cx| {
 3831        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3832    });
 3833    cx.assert_editor_state(indoc! {"
 3834        «TheQuickBrown
 3835        FoxJumpsOver
 3836        TheLazyDogˇ»
 3837    "});
 3838
 3839    // From here on out, test more complex cases of manipulate_text()
 3840
 3841    // Test no selection case - should affect words cursors are in
 3842    // Cursor at beginning, middle, and end of word
 3843    cx.set_state(indoc! {"
 3844        ˇhello big beauˇtiful worldˇ
 3845    "});
 3846    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3847    cx.assert_editor_state(indoc! {"
 3848        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3849    "});
 3850
 3851    // Test multiple selections on a single line and across multiple lines
 3852    cx.set_state(indoc! {"
 3853        «Theˇ» quick «brown
 3854        foxˇ» jumps «overˇ»
 3855        the «lazyˇ» dog
 3856    "});
 3857    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3858    cx.assert_editor_state(indoc! {"
 3859        «THEˇ» quick «BROWN
 3860        FOXˇ» jumps «OVERˇ»
 3861        the «LAZYˇ» dog
 3862    "});
 3863
 3864    // Test case where text length grows
 3865    cx.set_state(indoc! {"
 3866        «tschüߡ»
 3867    "});
 3868    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3869    cx.assert_editor_state(indoc! {"
 3870        «TSCHÜSSˇ»
 3871    "});
 3872
 3873    // Test to make sure we don't crash when text shrinks
 3874    cx.set_state(indoc! {"
 3875        aaa_bbbˇ
 3876    "});
 3877    cx.update_editor(|e, window, cx| {
 3878        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3879    });
 3880    cx.assert_editor_state(indoc! {"
 3881        «aaaBbbˇ»
 3882    "});
 3883
 3884    // Test to make sure we all aware of the fact that each word can grow and shrink
 3885    // Final selections should be aware of this fact
 3886    cx.set_state(indoc! {"
 3887        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3891    });
 3892    cx.assert_editor_state(indoc! {"
 3893        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3894    "});
 3895
 3896    cx.set_state(indoc! {"
 3897        «hElLo, WoRld!ˇ»
 3898    "});
 3899    cx.update_editor(|e, window, cx| {
 3900        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3901    });
 3902    cx.assert_editor_state(indoc! {"
 3903        «HeLlO, wOrLD!ˇ»
 3904    "});
 3905}
 3906
 3907#[gpui::test]
 3908fn test_duplicate_line(cx: &mut TestAppContext) {
 3909    init_test(cx, |_| {});
 3910
 3911    let editor = cx.add_window(|window, cx| {
 3912        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3913        build_editor(buffer, window, cx)
 3914    });
 3915    _ = editor.update(cx, |editor, window, cx| {
 3916        editor.change_selections(None, window, cx, |s| {
 3917            s.select_display_ranges([
 3918                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3919                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3920                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3921                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3922            ])
 3923        });
 3924        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3925        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3926        assert_eq!(
 3927            editor.selections.display_ranges(cx),
 3928            vec![
 3929                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3930                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3931                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3932                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3933            ]
 3934        );
 3935    });
 3936
 3937    let editor = cx.add_window(|window, cx| {
 3938        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3939        build_editor(buffer, window, cx)
 3940    });
 3941    _ = editor.update(cx, |editor, window, cx| {
 3942        editor.change_selections(None, window, cx, |s| {
 3943            s.select_display_ranges([
 3944                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3945                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3946            ])
 3947        });
 3948        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3949        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3950        assert_eq!(
 3951            editor.selections.display_ranges(cx),
 3952            vec![
 3953                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3954                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3955            ]
 3956        );
 3957    });
 3958
 3959    // With `move_upwards` the selections stay in place, except for
 3960    // the lines inserted above them
 3961    let editor = cx.add_window(|window, cx| {
 3962        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3963        build_editor(buffer, window, cx)
 3964    });
 3965    _ = editor.update(cx, |editor, window, cx| {
 3966        editor.change_selections(None, window, cx, |s| {
 3967            s.select_display_ranges([
 3968                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3969                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3970                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3971                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3972            ])
 3973        });
 3974        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3975        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3976        assert_eq!(
 3977            editor.selections.display_ranges(cx),
 3978            vec![
 3979                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3980                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3981                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3982                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3983            ]
 3984        );
 3985    });
 3986
 3987    let editor = cx.add_window(|window, cx| {
 3988        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3989        build_editor(buffer, window, cx)
 3990    });
 3991    _ = editor.update(cx, |editor, window, cx| {
 3992        editor.change_selections(None, window, cx, |s| {
 3993            s.select_display_ranges([
 3994                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3995                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3996            ])
 3997        });
 3998        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3999        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4000        assert_eq!(
 4001            editor.selections.display_ranges(cx),
 4002            vec![
 4003                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4004                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4005            ]
 4006        );
 4007    });
 4008
 4009    let editor = cx.add_window(|window, cx| {
 4010        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4011        build_editor(buffer, window, cx)
 4012    });
 4013    _ = editor.update(cx, |editor, window, cx| {
 4014        editor.change_selections(None, window, cx, |s| {
 4015            s.select_display_ranges([
 4016                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4017                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4018            ])
 4019        });
 4020        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4021        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4022        assert_eq!(
 4023            editor.selections.display_ranges(cx),
 4024            vec![
 4025                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4026                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4027            ]
 4028        );
 4029    });
 4030}
 4031
 4032#[gpui::test]
 4033fn test_move_line_up_down(cx: &mut TestAppContext) {
 4034    init_test(cx, |_| {});
 4035
 4036    let editor = cx.add_window(|window, cx| {
 4037        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4038        build_editor(buffer, window, cx)
 4039    });
 4040    _ = editor.update(cx, |editor, window, cx| {
 4041        editor.fold_creases(
 4042            vec![
 4043                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4044                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4045                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4046            ],
 4047            true,
 4048            window,
 4049            cx,
 4050        );
 4051        editor.change_selections(None, window, cx, |s| {
 4052            s.select_display_ranges([
 4053                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4054                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4055                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4056                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4057            ])
 4058        });
 4059        assert_eq!(
 4060            editor.display_text(cx),
 4061            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4062        );
 4063
 4064        editor.move_line_up(&MoveLineUp, window, cx);
 4065        assert_eq!(
 4066            editor.display_text(cx),
 4067            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4068        );
 4069        assert_eq!(
 4070            editor.selections.display_ranges(cx),
 4071            vec![
 4072                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4073                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4074                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4075                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4076            ]
 4077        );
 4078    });
 4079
 4080    _ = editor.update(cx, |editor, window, cx| {
 4081        editor.move_line_down(&MoveLineDown, window, cx);
 4082        assert_eq!(
 4083            editor.display_text(cx),
 4084            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4085        );
 4086        assert_eq!(
 4087            editor.selections.display_ranges(cx),
 4088            vec![
 4089                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4090                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4091                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4092                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4093            ]
 4094        );
 4095    });
 4096
 4097    _ = editor.update(cx, |editor, window, cx| {
 4098        editor.move_line_down(&MoveLineDown, window, cx);
 4099        assert_eq!(
 4100            editor.display_text(cx),
 4101            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4102        );
 4103        assert_eq!(
 4104            editor.selections.display_ranges(cx),
 4105            vec![
 4106                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4107                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4108                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4109                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4110            ]
 4111        );
 4112    });
 4113
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.move_line_up(&MoveLineUp, window, cx);
 4116        assert_eq!(
 4117            editor.display_text(cx),
 4118            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4119        );
 4120        assert_eq!(
 4121            editor.selections.display_ranges(cx),
 4122            vec![
 4123                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4124                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4125                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4126                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4127            ]
 4128        );
 4129    });
 4130}
 4131
 4132#[gpui::test]
 4133fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4134    init_test(cx, |_| {});
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4142        editor.insert_blocks(
 4143            [BlockProperties {
 4144                style: BlockStyle::Fixed,
 4145                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4146                height: 1,
 4147                render: Arc::new(|_| div().into_any()),
 4148                priority: 0,
 4149            }],
 4150            Some(Autoscroll::fit()),
 4151            cx,
 4152        );
 4153        editor.change_selections(None, window, cx, |s| {
 4154            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4155        });
 4156        editor.move_line_down(&MoveLineDown, window, cx);
 4157    });
 4158}
 4159
 4160#[gpui::test]
 4161async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4162    init_test(cx, |_| {});
 4163
 4164    let mut cx = EditorTestContext::new(cx).await;
 4165    cx.set_state(
 4166        &"
 4167            ˇzero
 4168            one
 4169            two
 4170            three
 4171            four
 4172            five
 4173        "
 4174        .unindent(),
 4175    );
 4176
 4177    // Create a four-line block that replaces three lines of text.
 4178    cx.update_editor(|editor, window, cx| {
 4179        let snapshot = editor.snapshot(window, cx);
 4180        let snapshot = &snapshot.buffer_snapshot;
 4181        let placement = BlockPlacement::Replace(
 4182            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4183        );
 4184        editor.insert_blocks(
 4185            [BlockProperties {
 4186                placement,
 4187                height: 4,
 4188                style: BlockStyle::Sticky,
 4189                render: Arc::new(|_| gpui::div().into_any_element()),
 4190                priority: 0,
 4191            }],
 4192            None,
 4193            cx,
 4194        );
 4195    });
 4196
 4197    // Move down so that the cursor touches the block.
 4198    cx.update_editor(|editor, window, cx| {
 4199        editor.move_down(&Default::default(), window, cx);
 4200    });
 4201    cx.assert_editor_state(
 4202        &"
 4203            zero
 4204            «one
 4205            two
 4206            threeˇ»
 4207            four
 4208            five
 4209        "
 4210        .unindent(),
 4211    );
 4212
 4213    // Move down past the block.
 4214    cx.update_editor(|editor, window, cx| {
 4215        editor.move_down(&Default::default(), window, cx);
 4216    });
 4217    cx.assert_editor_state(
 4218        &"
 4219            zero
 4220            one
 4221            two
 4222            three
 4223            ˇfour
 4224            five
 4225        "
 4226        .unindent(),
 4227    );
 4228}
 4229
 4230#[gpui::test]
 4231fn test_transpose(cx: &mut TestAppContext) {
 4232    init_test(cx, |_| {});
 4233
 4234    _ = cx.add_window(|window, cx| {
 4235        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4236        editor.set_style(EditorStyle::default(), window, cx);
 4237        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4238        editor.transpose(&Default::default(), window, cx);
 4239        assert_eq!(editor.text(cx), "bac");
 4240        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4241
 4242        editor.transpose(&Default::default(), window, cx);
 4243        assert_eq!(editor.text(cx), "bca");
 4244        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4245
 4246        editor.transpose(&Default::default(), window, cx);
 4247        assert_eq!(editor.text(cx), "bac");
 4248        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4249
 4250        editor
 4251    });
 4252
 4253    _ = cx.add_window(|window, cx| {
 4254        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4255        editor.set_style(EditorStyle::default(), window, cx);
 4256        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4257        editor.transpose(&Default::default(), window, cx);
 4258        assert_eq!(editor.text(cx), "acb\nde");
 4259        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4260
 4261        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4262        editor.transpose(&Default::default(), window, cx);
 4263        assert_eq!(editor.text(cx), "acbd\ne");
 4264        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4265
 4266        editor.transpose(&Default::default(), window, cx);
 4267        assert_eq!(editor.text(cx), "acbde\n");
 4268        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4269
 4270        editor.transpose(&Default::default(), window, cx);
 4271        assert_eq!(editor.text(cx), "acbd\ne");
 4272        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4273
 4274        editor
 4275    });
 4276
 4277    _ = cx.add_window(|window, cx| {
 4278        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4279        editor.set_style(EditorStyle::default(), window, cx);
 4280        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4281        editor.transpose(&Default::default(), window, cx);
 4282        assert_eq!(editor.text(cx), "bacd\ne");
 4283        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4284
 4285        editor.transpose(&Default::default(), window, cx);
 4286        assert_eq!(editor.text(cx), "bcade\n");
 4287        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4288
 4289        editor.transpose(&Default::default(), window, cx);
 4290        assert_eq!(editor.text(cx), "bcda\ne");
 4291        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4292
 4293        editor.transpose(&Default::default(), window, cx);
 4294        assert_eq!(editor.text(cx), "bcade\n");
 4295        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4296
 4297        editor.transpose(&Default::default(), window, cx);
 4298        assert_eq!(editor.text(cx), "bcaed\n");
 4299        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4300
 4301        editor
 4302    });
 4303
 4304    _ = cx.add_window(|window, cx| {
 4305        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4306        editor.set_style(EditorStyle::default(), window, cx);
 4307        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4308        editor.transpose(&Default::default(), window, cx);
 4309        assert_eq!(editor.text(cx), "🏀🍐✋");
 4310        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4311
 4312        editor.transpose(&Default::default(), window, cx);
 4313        assert_eq!(editor.text(cx), "🏀✋🍐");
 4314        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4315
 4316        editor.transpose(&Default::default(), window, cx);
 4317        assert_eq!(editor.text(cx), "🏀🍐✋");
 4318        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4319
 4320        editor
 4321    });
 4322}
 4323
 4324#[gpui::test]
 4325async fn test_rewrap(cx: &mut TestAppContext) {
 4326    init_test(cx, |settings| {
 4327        settings.languages.extend([
 4328            (
 4329                "Markdown".into(),
 4330                LanguageSettingsContent {
 4331                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4332                    ..Default::default()
 4333                },
 4334            ),
 4335            (
 4336                "Plain Text".into(),
 4337                LanguageSettingsContent {
 4338                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4339                    ..Default::default()
 4340                },
 4341            ),
 4342        ])
 4343    });
 4344
 4345    let mut cx = EditorTestContext::new(cx).await;
 4346
 4347    let language_with_c_comments = Arc::new(Language::new(
 4348        LanguageConfig {
 4349            line_comments: vec!["// ".into()],
 4350            ..LanguageConfig::default()
 4351        },
 4352        None,
 4353    ));
 4354    let language_with_pound_comments = Arc::new(Language::new(
 4355        LanguageConfig {
 4356            line_comments: vec!["# ".into()],
 4357            ..LanguageConfig::default()
 4358        },
 4359        None,
 4360    ));
 4361    let markdown_language = Arc::new(Language::new(
 4362        LanguageConfig {
 4363            name: "Markdown".into(),
 4364            ..LanguageConfig::default()
 4365        },
 4366        None,
 4367    ));
 4368    let language_with_doc_comments = Arc::new(Language::new(
 4369        LanguageConfig {
 4370            line_comments: vec!["// ".into(), "/// ".into()],
 4371            ..LanguageConfig::default()
 4372        },
 4373        Some(tree_sitter_rust::LANGUAGE.into()),
 4374    ));
 4375
 4376    let plaintext_language = Arc::new(Language::new(
 4377        LanguageConfig {
 4378            name: "Plain Text".into(),
 4379            ..LanguageConfig::default()
 4380        },
 4381        None,
 4382    ));
 4383
 4384    assert_rewrap(
 4385        indoc! {"
 4386            // ˇ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.
 4387        "},
 4388        indoc! {"
 4389            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4390            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4391            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4392            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4393            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4394            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4395            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4396            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4397            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4398            // porttitor id. Aliquam id accumsan eros.
 4399        "},
 4400        language_with_c_comments.clone(),
 4401        &mut cx,
 4402    );
 4403
 4404    // Test that rewrapping works inside of a selection
 4405    assert_rewrap(
 4406        indoc! {"
 4407            «// 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.ˇ»
 4408        "},
 4409        indoc! {"
 4410            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4411            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4412            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4413            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4414            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4415            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4416            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4417            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4418            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4419            // porttitor id. Aliquam id accumsan eros.ˇ»
 4420        "},
 4421        language_with_c_comments.clone(),
 4422        &mut cx,
 4423    );
 4424
 4425    // Test that cursors that expand to the same region are collapsed.
 4426    assert_rewrap(
 4427        indoc! {"
 4428            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4429            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4430            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4431            // ˇ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.
 4432        "},
 4433        indoc! {"
 4434            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4435            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4436            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4437            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4438            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4439            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4440            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4441            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4442            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4443            // porttitor id. Aliquam id accumsan eros.
 4444        "},
 4445        language_with_c_comments.clone(),
 4446        &mut cx,
 4447    );
 4448
 4449    // Test that non-contiguous selections are treated separately.
 4450    assert_rewrap(
 4451        indoc! {"
 4452            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4453            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4454            //
 4455            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4456            // ˇ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.
 4457        "},
 4458        indoc! {"
 4459            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4460            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4461            // auctor, eu lacinia sapien scelerisque.
 4462            //
 4463            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4464            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4465            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4466            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4467            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4468            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4469            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4470        "},
 4471        language_with_c_comments.clone(),
 4472        &mut cx,
 4473    );
 4474
 4475    // Test that different comment prefixes are supported.
 4476    assert_rewrap(
 4477        indoc! {"
 4478            # ˇ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.
 4479        "},
 4480        indoc! {"
 4481            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4482            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4483            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4484            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4485            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4486            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4487            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4488            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4489            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4490            # accumsan eros.
 4491        "},
 4492        language_with_pound_comments.clone(),
 4493        &mut cx,
 4494    );
 4495
 4496    // Test that rewrapping is ignored outside of comments in most languages.
 4497    assert_rewrap(
 4498        indoc! {"
 4499            /// Adds two numbers.
 4500            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4501            fn add(a: u32, b: u32) -> u32 {
 4502                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ˇ
 4503            }
 4504        "},
 4505        indoc! {"
 4506            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4507            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4508            fn add(a: u32, b: u32) -> u32 {
 4509                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ˇ
 4510            }
 4511        "},
 4512        language_with_doc_comments.clone(),
 4513        &mut cx,
 4514    );
 4515
 4516    // Test that rewrapping works in Markdown and Plain Text languages.
 4517    assert_rewrap(
 4518        indoc! {"
 4519            # Hello
 4520
 4521            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.
 4522        "},
 4523        indoc! {"
 4524            # Hello
 4525
 4526            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4527            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4528            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4529            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4530            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4531            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4532            Integer sit amet scelerisque nisi.
 4533        "},
 4534        markdown_language,
 4535        &mut cx,
 4536    );
 4537
 4538    assert_rewrap(
 4539        indoc! {"
 4540            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.
 4541        "},
 4542        indoc! {"
 4543            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4544            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4545            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4546            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4547            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4548            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4549            Integer sit amet scelerisque nisi.
 4550        "},
 4551        plaintext_language,
 4552        &mut cx,
 4553    );
 4554
 4555    // Test rewrapping unaligned comments in a selection.
 4556    assert_rewrap(
 4557        indoc! {"
 4558            fn foo() {
 4559                if true {
 4560            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4561            // Praesent semper egestas tellus id dignissim.ˇ»
 4562                    do_something();
 4563                } else {
 4564                    //
 4565                }
 4566            }
 4567        "},
 4568        indoc! {"
 4569            fn foo() {
 4570                if true {
 4571            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4572                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4573                    // egestas tellus id dignissim.ˇ»
 4574                    do_something();
 4575                } else {
 4576                    //
 4577                }
 4578            }
 4579        "},
 4580        language_with_doc_comments.clone(),
 4581        &mut cx,
 4582    );
 4583
 4584    assert_rewrap(
 4585        indoc! {"
 4586            fn foo() {
 4587                if true {
 4588            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4589            // Praesent semper egestas tellus id dignissim.»
 4590                    do_something();
 4591                } else {
 4592                    //
 4593                }
 4594
 4595            }
 4596        "},
 4597        indoc! {"
 4598            fn foo() {
 4599                if true {
 4600            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4601                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4602                    // egestas tellus id dignissim.»
 4603                    do_something();
 4604                } else {
 4605                    //
 4606                }
 4607
 4608            }
 4609        "},
 4610        language_with_doc_comments.clone(),
 4611        &mut cx,
 4612    );
 4613
 4614    #[track_caller]
 4615    fn assert_rewrap(
 4616        unwrapped_text: &str,
 4617        wrapped_text: &str,
 4618        language: Arc<Language>,
 4619        cx: &mut EditorTestContext,
 4620    ) {
 4621        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4622        cx.set_state(unwrapped_text);
 4623        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4624        cx.assert_editor_state(wrapped_text);
 4625    }
 4626}
 4627
 4628#[gpui::test]
 4629async fn test_clipboard(cx: &mut TestAppContext) {
 4630    init_test(cx, |_| {});
 4631
 4632    let mut cx = EditorTestContext::new(cx).await;
 4633
 4634    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4635    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4636    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4637
 4638    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4639    cx.set_state("two ˇfour ˇsix ˇ");
 4640    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4641    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4642
 4643    // Paste again but with only two cursors. Since the number of cursors doesn't
 4644    // match the number of slices in the clipboard, the entire clipboard text
 4645    // is pasted at each cursor.
 4646    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4647    cx.update_editor(|e, window, cx| {
 4648        e.handle_input("( ", window, cx);
 4649        e.paste(&Paste, window, cx);
 4650        e.handle_input(") ", window, cx);
 4651    });
 4652    cx.assert_editor_state(
 4653        &([
 4654            "( one✅ ",
 4655            "three ",
 4656            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4657            "three ",
 4658            "five ) ˇ",
 4659        ]
 4660        .join("\n")),
 4661    );
 4662
 4663    // Cut with three selections, one of which is full-line.
 4664    cx.set_state(indoc! {"
 4665        1«2ˇ»3
 4666        4ˇ567
 4667        «8ˇ»9"});
 4668    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4669    cx.assert_editor_state(indoc! {"
 4670        1ˇ3
 4671        ˇ9"});
 4672
 4673    // Paste with three selections, noticing how the copied selection that was full-line
 4674    // gets inserted before the second cursor.
 4675    cx.set_state(indoc! {"
 4676        1ˇ3
 4677 4678        «oˇ»ne"});
 4679    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4680    cx.assert_editor_state(indoc! {"
 4681        12ˇ3
 4682        4567
 4683 4684        8ˇne"});
 4685
 4686    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4687    cx.set_state(indoc! {"
 4688        The quick brown
 4689        fox juˇmps over
 4690        the lazy dog"});
 4691    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4692    assert_eq!(
 4693        cx.read_from_clipboard()
 4694            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4695        Some("fox jumps over\n".to_string())
 4696    );
 4697
 4698    // Paste with three selections, noticing how the copied full-line selection is inserted
 4699    // before the empty selections but replaces the selection that is non-empty.
 4700    cx.set_state(indoc! {"
 4701        Tˇhe quick brown
 4702        «foˇ»x jumps over
 4703        tˇhe lazy dog"});
 4704    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4705    cx.assert_editor_state(indoc! {"
 4706        fox jumps over
 4707        Tˇhe quick brown
 4708        fox jumps over
 4709        ˇx jumps over
 4710        fox jumps over
 4711        tˇhe lazy dog"});
 4712}
 4713
 4714#[gpui::test]
 4715async fn test_paste_multiline(cx: &mut TestAppContext) {
 4716    init_test(cx, |_| {});
 4717
 4718    let mut cx = EditorTestContext::new(cx).await;
 4719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4720
 4721    // Cut an indented block, without the leading whitespace.
 4722    cx.set_state(indoc! {"
 4723        const a: B = (
 4724            c(),
 4725            «d(
 4726                e,
 4727                f
 4728            )ˇ»
 4729        );
 4730    "});
 4731    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4732    cx.assert_editor_state(indoc! {"
 4733        const a: B = (
 4734            c(),
 4735            ˇ
 4736        );
 4737    "});
 4738
 4739    // Paste it at the same position.
 4740    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4741    cx.assert_editor_state(indoc! {"
 4742        const a: B = (
 4743            c(),
 4744            d(
 4745                e,
 4746                f
 4747 4748        );
 4749    "});
 4750
 4751    // Paste it at a line with a lower indent level.
 4752    cx.set_state(indoc! {"
 4753        ˇ
 4754        const a: B = (
 4755            c(),
 4756        );
 4757    "});
 4758    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4759    cx.assert_editor_state(indoc! {"
 4760        d(
 4761            e,
 4762            f
 4763 4764        const a: B = (
 4765            c(),
 4766        );
 4767    "});
 4768
 4769    // Cut an indented block, with the leading whitespace.
 4770    cx.set_state(indoc! {"
 4771        const a: B = (
 4772            c(),
 4773        «    d(
 4774                e,
 4775                f
 4776            )
 4777        ˇ»);
 4778    "});
 4779    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4780    cx.assert_editor_state(indoc! {"
 4781        const a: B = (
 4782            c(),
 4783        ˇ);
 4784    "});
 4785
 4786    // Paste it at the same position.
 4787    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4788    cx.assert_editor_state(indoc! {"
 4789        const a: B = (
 4790            c(),
 4791            d(
 4792                e,
 4793                f
 4794            )
 4795        ˇ);
 4796    "});
 4797
 4798    // Paste it at a line with a higher indent level.
 4799    cx.set_state(indoc! {"
 4800        const a: B = (
 4801            c(),
 4802            d(
 4803                e,
 4804 4805            )
 4806        );
 4807    "});
 4808    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4809    cx.assert_editor_state(indoc! {"
 4810        const a: B = (
 4811            c(),
 4812            d(
 4813                e,
 4814                f    d(
 4815                    e,
 4816                    f
 4817                )
 4818        ˇ
 4819            )
 4820        );
 4821    "});
 4822}
 4823
 4824#[gpui::test]
 4825async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4826    init_test(cx, |_| {});
 4827
 4828    cx.write_to_clipboard(ClipboardItem::new_string(
 4829        "    d(\n        e\n    );\n".into(),
 4830    ));
 4831
 4832    let mut cx = EditorTestContext::new(cx).await;
 4833    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4834
 4835    cx.set_state(indoc! {"
 4836        fn a() {
 4837            b();
 4838            if c() {
 4839                ˇ
 4840            }
 4841        }
 4842    "});
 4843
 4844    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4845    cx.assert_editor_state(indoc! {"
 4846        fn a() {
 4847            b();
 4848            if c() {
 4849                d(
 4850                    e
 4851                );
 4852        ˇ
 4853            }
 4854        }
 4855    "});
 4856
 4857    cx.set_state(indoc! {"
 4858        fn a() {
 4859            b();
 4860            ˇ
 4861        }
 4862    "});
 4863
 4864    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4865    cx.assert_editor_state(indoc! {"
 4866        fn a() {
 4867            b();
 4868            d(
 4869                e
 4870            );
 4871        ˇ
 4872        }
 4873    "});
 4874}
 4875
 4876#[gpui::test]
 4877fn test_select_all(cx: &mut TestAppContext) {
 4878    init_test(cx, |_| {});
 4879
 4880    let editor = cx.add_window(|window, cx| {
 4881        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4882        build_editor(buffer, window, cx)
 4883    });
 4884    _ = editor.update(cx, |editor, window, cx| {
 4885        editor.select_all(&SelectAll, window, cx);
 4886        assert_eq!(
 4887            editor.selections.display_ranges(cx),
 4888            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4889        );
 4890    });
 4891}
 4892
 4893#[gpui::test]
 4894fn test_select_line(cx: &mut TestAppContext) {
 4895    init_test(cx, |_| {});
 4896
 4897    let editor = cx.add_window(|window, cx| {
 4898        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4899        build_editor(buffer, window, cx)
 4900    });
 4901    _ = editor.update(cx, |editor, window, cx| {
 4902        editor.change_selections(None, window, cx, |s| {
 4903            s.select_display_ranges([
 4904                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4905                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4906                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4907                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4908            ])
 4909        });
 4910        editor.select_line(&SelectLine, window, cx);
 4911        assert_eq!(
 4912            editor.selections.display_ranges(cx),
 4913            vec![
 4914                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4915                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4916            ]
 4917        );
 4918    });
 4919
 4920    _ = editor.update(cx, |editor, window, cx| {
 4921        editor.select_line(&SelectLine, window, cx);
 4922        assert_eq!(
 4923            editor.selections.display_ranges(cx),
 4924            vec![
 4925                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4926                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4927            ]
 4928        );
 4929    });
 4930
 4931    _ = editor.update(cx, |editor, window, cx| {
 4932        editor.select_line(&SelectLine, window, cx);
 4933        assert_eq!(
 4934            editor.selections.display_ranges(cx),
 4935            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4936        );
 4937    });
 4938}
 4939
 4940#[gpui::test]
 4941async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4942    init_test(cx, |_| {});
 4943    let mut cx = EditorTestContext::new(cx).await;
 4944
 4945    #[track_caller]
 4946    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4947        cx.set_state(initial_state);
 4948        cx.update_editor(|e, window, cx| {
 4949            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4950        });
 4951        cx.assert_editor_state(expected_state);
 4952    }
 4953
 4954    // Selection starts and ends at the middle of lines, left-to-right
 4955    test(
 4956        &mut cx,
 4957        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4958        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4959    );
 4960    // Same thing, right-to-left
 4961    test(
 4962        &mut cx,
 4963        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4964        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4965    );
 4966
 4967    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4968    test(
 4969        &mut cx,
 4970        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4971        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4972    );
 4973    // Same thing, right-to-left
 4974    test(
 4975        &mut cx,
 4976        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4977        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4978    );
 4979
 4980    // Whole buffer, left-to-right, last line ends with newline
 4981    test(
 4982        &mut cx,
 4983        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4984        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4985    );
 4986    // Same thing, right-to-left
 4987    test(
 4988        &mut cx,
 4989        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4990        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4991    );
 4992
 4993    // Starts at the end of a line, ends at the start of another
 4994    test(
 4995        &mut cx,
 4996        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4997        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4998    );
 4999}
 5000
 5001#[gpui::test]
 5002async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5003    init_test(cx, |_| {});
 5004
 5005    let editor = cx.add_window(|window, cx| {
 5006        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5007        build_editor(buffer, window, cx)
 5008    });
 5009
 5010    // setup
 5011    _ = editor.update(cx, |editor, window, cx| {
 5012        editor.fold_creases(
 5013            vec![
 5014                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5015                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5016                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5017            ],
 5018            true,
 5019            window,
 5020            cx,
 5021        );
 5022        assert_eq!(
 5023            editor.display_text(cx),
 5024            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5025        );
 5026    });
 5027
 5028    _ = editor.update(cx, |editor, window, cx| {
 5029        editor.change_selections(None, window, cx, |s| {
 5030            s.select_display_ranges([
 5031                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5032                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5033                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5034                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5035            ])
 5036        });
 5037        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5038        assert_eq!(
 5039            editor.display_text(cx),
 5040            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5041        );
 5042    });
 5043    EditorTestContext::for_editor(editor, cx)
 5044        .await
 5045        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5046
 5047    _ = editor.update(cx, |editor, window, cx| {
 5048        editor.change_selections(None, window, cx, |s| {
 5049            s.select_display_ranges([
 5050                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5051            ])
 5052        });
 5053        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5054        assert_eq!(
 5055            editor.display_text(cx),
 5056            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5057        );
 5058        assert_eq!(
 5059            editor.selections.display_ranges(cx),
 5060            [
 5061                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5062                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5063                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5064                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5065                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5066                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5067                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5068            ]
 5069        );
 5070    });
 5071    EditorTestContext::for_editor(editor, cx)
 5072        .await
 5073        .assert_editor_state(
 5074            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5075        );
 5076}
 5077
 5078#[gpui::test]
 5079async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5080    init_test(cx, |_| {});
 5081
 5082    let mut cx = EditorTestContext::new(cx).await;
 5083
 5084    cx.set_state(indoc!(
 5085        r#"abc
 5086           defˇghi
 5087
 5088           jk
 5089           nlmo
 5090           "#
 5091    ));
 5092
 5093    cx.update_editor(|editor, window, cx| {
 5094        editor.add_selection_above(&Default::default(), window, cx);
 5095    });
 5096
 5097    cx.assert_editor_state(indoc!(
 5098        r#"abcˇ
 5099           defˇghi
 5100
 5101           jk
 5102           nlmo
 5103           "#
 5104    ));
 5105
 5106    cx.update_editor(|editor, window, cx| {
 5107        editor.add_selection_above(&Default::default(), window, cx);
 5108    });
 5109
 5110    cx.assert_editor_state(indoc!(
 5111        r#"abcˇ
 5112            defˇghi
 5113
 5114            jk
 5115            nlmo
 5116            "#
 5117    ));
 5118
 5119    cx.update_editor(|editor, window, cx| {
 5120        editor.add_selection_below(&Default::default(), window, cx);
 5121    });
 5122
 5123    cx.assert_editor_state(indoc!(
 5124        r#"abc
 5125           defˇghi
 5126
 5127           jk
 5128           nlmo
 5129           "#
 5130    ));
 5131
 5132    cx.update_editor(|editor, window, cx| {
 5133        editor.undo_selection(&Default::default(), window, cx);
 5134    });
 5135
 5136    cx.assert_editor_state(indoc!(
 5137        r#"abcˇ
 5138           defˇghi
 5139
 5140           jk
 5141           nlmo
 5142           "#
 5143    ));
 5144
 5145    cx.update_editor(|editor, window, cx| {
 5146        editor.redo_selection(&Default::default(), window, cx);
 5147    });
 5148
 5149    cx.assert_editor_state(indoc!(
 5150        r#"abc
 5151           defˇghi
 5152
 5153           jk
 5154           nlmo
 5155           "#
 5156    ));
 5157
 5158    cx.update_editor(|editor, window, cx| {
 5159        editor.add_selection_below(&Default::default(), window, cx);
 5160    });
 5161
 5162    cx.assert_editor_state(indoc!(
 5163        r#"abc
 5164           defˇghi
 5165
 5166           jk
 5167           nlmˇo
 5168           "#
 5169    ));
 5170
 5171    cx.update_editor(|editor, window, cx| {
 5172        editor.add_selection_below(&Default::default(), window, cx);
 5173    });
 5174
 5175    cx.assert_editor_state(indoc!(
 5176        r#"abc
 5177           defˇghi
 5178
 5179           jk
 5180           nlmˇo
 5181           "#
 5182    ));
 5183
 5184    // change selections
 5185    cx.set_state(indoc!(
 5186        r#"abc
 5187           def«ˇg»hi
 5188
 5189           jk
 5190           nlmo
 5191           "#
 5192    ));
 5193
 5194    cx.update_editor(|editor, window, cx| {
 5195        editor.add_selection_below(&Default::default(), window, cx);
 5196    });
 5197
 5198    cx.assert_editor_state(indoc!(
 5199        r#"abc
 5200           def«ˇg»hi
 5201
 5202           jk
 5203           nlm«ˇo»
 5204           "#
 5205    ));
 5206
 5207    cx.update_editor(|editor, window, cx| {
 5208        editor.add_selection_below(&Default::default(), window, cx);
 5209    });
 5210
 5211    cx.assert_editor_state(indoc!(
 5212        r#"abc
 5213           def«ˇg»hi
 5214
 5215           jk
 5216           nlm«ˇo»
 5217           "#
 5218    ));
 5219
 5220    cx.update_editor(|editor, window, cx| {
 5221        editor.add_selection_above(&Default::default(), window, cx);
 5222    });
 5223
 5224    cx.assert_editor_state(indoc!(
 5225        r#"abc
 5226           def«ˇg»hi
 5227
 5228           jk
 5229           nlmo
 5230           "#
 5231    ));
 5232
 5233    cx.update_editor(|editor, window, cx| {
 5234        editor.add_selection_above(&Default::default(), window, cx);
 5235    });
 5236
 5237    cx.assert_editor_state(indoc!(
 5238        r#"abc
 5239           def«ˇg»hi
 5240
 5241           jk
 5242           nlmo
 5243           "#
 5244    ));
 5245
 5246    // Change selections again
 5247    cx.set_state(indoc!(
 5248        r#"a«bc
 5249           defgˇ»hi
 5250
 5251           jk
 5252           nlmo
 5253           "#
 5254    ));
 5255
 5256    cx.update_editor(|editor, window, cx| {
 5257        editor.add_selection_below(&Default::default(), window, cx);
 5258    });
 5259
 5260    cx.assert_editor_state(indoc!(
 5261        r#"a«bcˇ»
 5262           d«efgˇ»hi
 5263
 5264           j«kˇ»
 5265           nlmo
 5266           "#
 5267    ));
 5268
 5269    cx.update_editor(|editor, window, cx| {
 5270        editor.add_selection_below(&Default::default(), window, cx);
 5271    });
 5272    cx.assert_editor_state(indoc!(
 5273        r#"a«bcˇ»
 5274           d«efgˇ»hi
 5275
 5276           j«kˇ»
 5277           n«lmoˇ»
 5278           "#
 5279    ));
 5280    cx.update_editor(|editor, window, cx| {
 5281        editor.add_selection_above(&Default::default(), window, cx);
 5282    });
 5283
 5284    cx.assert_editor_state(indoc!(
 5285        r#"a«bcˇ»
 5286           d«efgˇ»hi
 5287
 5288           j«kˇ»
 5289           nlmo
 5290           "#
 5291    ));
 5292
 5293    // Change selections again
 5294    cx.set_state(indoc!(
 5295        r#"abc
 5296           d«ˇefghi
 5297
 5298           jk
 5299           nlm»o
 5300           "#
 5301    ));
 5302
 5303    cx.update_editor(|editor, window, cx| {
 5304        editor.add_selection_above(&Default::default(), window, cx);
 5305    });
 5306
 5307    cx.assert_editor_state(indoc!(
 5308        r#"a«ˇbc»
 5309           d«ˇef»ghi
 5310
 5311           j«ˇk»
 5312           n«ˇlm»o
 5313           "#
 5314    ));
 5315
 5316    cx.update_editor(|editor, window, cx| {
 5317        editor.add_selection_below(&Default::default(), window, cx);
 5318    });
 5319
 5320    cx.assert_editor_state(indoc!(
 5321        r#"abc
 5322           d«ˇef»ghi
 5323
 5324           j«ˇk»
 5325           n«ˇlm»o
 5326           "#
 5327    ));
 5328}
 5329
 5330#[gpui::test]
 5331async fn test_select_next(cx: &mut TestAppContext) {
 5332    init_test(cx, |_| {});
 5333
 5334    let mut cx = EditorTestContext::new(cx).await;
 5335    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5336
 5337    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5338        .unwrap();
 5339    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5340
 5341    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5342        .unwrap();
 5343    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5344
 5345    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5346    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5347
 5348    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5349    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5350
 5351    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5352        .unwrap();
 5353    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5354
 5355    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5356        .unwrap();
 5357    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5358}
 5359
 5360#[gpui::test]
 5361async fn test_select_all_matches(cx: &mut TestAppContext) {
 5362    init_test(cx, |_| {});
 5363
 5364    let mut cx = EditorTestContext::new(cx).await;
 5365
 5366    // Test caret-only selections
 5367    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5368    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5369        .unwrap();
 5370    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5371
 5372    // Test left-to-right selections
 5373    cx.set_state("abc\n«abcˇ»\nabc");
 5374    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5375        .unwrap();
 5376    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5377
 5378    // Test right-to-left selections
 5379    cx.set_state("abc\n«ˇabc»\nabc");
 5380    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5381        .unwrap();
 5382    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5383
 5384    // Test selecting whitespace with caret selection
 5385    cx.set_state("abc\nˇ   abc\nabc");
 5386    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5387        .unwrap();
 5388    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5389
 5390    // Test selecting whitespace with left-to-right selection
 5391    cx.set_state("abc\n«ˇ  »abc\nabc");
 5392    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5393        .unwrap();
 5394    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5395
 5396    // Test no matches with right-to-left selection
 5397    cx.set_state("abc\n«  ˇ»abc\nabc");
 5398    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5399        .unwrap();
 5400    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5401}
 5402
 5403#[gpui::test]
 5404async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5405    init_test(cx, |_| {});
 5406
 5407    let mut cx = EditorTestContext::new(cx).await;
 5408    cx.set_state(
 5409        r#"let foo = 2;
 5410lˇet foo = 2;
 5411let fooˇ = 2;
 5412let foo = 2;
 5413let foo = ˇ2;"#,
 5414    );
 5415
 5416    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5417        .unwrap();
 5418    cx.assert_editor_state(
 5419        r#"let foo = 2;
 5420«letˇ» foo = 2;
 5421let «fooˇ» = 2;
 5422let foo = 2;
 5423let foo = «2ˇ»;"#,
 5424    );
 5425
 5426    // noop for multiple selections with different contents
 5427    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5428        .unwrap();
 5429    cx.assert_editor_state(
 5430        r#"let foo = 2;
 5431«letˇ» foo = 2;
 5432let «fooˇ» = 2;
 5433let foo = 2;
 5434let foo = «2ˇ»;"#,
 5435    );
 5436}
 5437
 5438#[gpui::test]
 5439async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5440    init_test(cx, |_| {});
 5441
 5442    let mut cx =
 5443        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5444
 5445    cx.assert_editor_state(indoc! {"
 5446        ˇbbb
 5447        ccc
 5448
 5449        bbb
 5450        ccc
 5451        "});
 5452    cx.dispatch_action(SelectPrevious::default());
 5453    cx.assert_editor_state(indoc! {"
 5454                «bbbˇ»
 5455                ccc
 5456
 5457                bbb
 5458                ccc
 5459                "});
 5460    cx.dispatch_action(SelectPrevious::default());
 5461    cx.assert_editor_state(indoc! {"
 5462                «bbbˇ»
 5463                ccc
 5464
 5465                «bbbˇ»
 5466                ccc
 5467                "});
 5468}
 5469
 5470#[gpui::test]
 5471async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5472    init_test(cx, |_| {});
 5473
 5474    let mut cx = EditorTestContext::new(cx).await;
 5475    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5476
 5477    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5478        .unwrap();
 5479    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5480
 5481    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5482        .unwrap();
 5483    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5484
 5485    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5486    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5487
 5488    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5489    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5490
 5491    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5492        .unwrap();
 5493    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5494
 5495    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5496        .unwrap();
 5497    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5498
 5499    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5500        .unwrap();
 5501    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5502}
 5503
 5504#[gpui::test]
 5505async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5506    init_test(cx, |_| {});
 5507
 5508    let mut cx = EditorTestContext::new(cx).await;
 5509    cx.set_state("");
 5510
 5511    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5512        .unwrap();
 5513    cx.assert_editor_state("«aˇ»");
 5514    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5515        .unwrap();
 5516    cx.assert_editor_state("«aˇ»");
 5517}
 5518
 5519#[gpui::test]
 5520async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5521    init_test(cx, |_| {});
 5522
 5523    let mut cx = EditorTestContext::new(cx).await;
 5524    cx.set_state(
 5525        r#"let foo = 2;
 5526lˇet foo = 2;
 5527let fooˇ = 2;
 5528let foo = 2;
 5529let foo = ˇ2;"#,
 5530    );
 5531
 5532    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5533        .unwrap();
 5534    cx.assert_editor_state(
 5535        r#"let foo = 2;
 5536«letˇ» foo = 2;
 5537let «fooˇ» = 2;
 5538let foo = 2;
 5539let foo = «2ˇ»;"#,
 5540    );
 5541
 5542    // noop for multiple selections with different contents
 5543    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5544        .unwrap();
 5545    cx.assert_editor_state(
 5546        r#"let foo = 2;
 5547«letˇ» foo = 2;
 5548let «fooˇ» = 2;
 5549let foo = 2;
 5550let foo = «2ˇ»;"#,
 5551    );
 5552}
 5553
 5554#[gpui::test]
 5555async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5556    init_test(cx, |_| {});
 5557
 5558    let mut cx = EditorTestContext::new(cx).await;
 5559    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5560
 5561    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5562        .unwrap();
 5563    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5564
 5565    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5566        .unwrap();
 5567    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5568
 5569    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5570    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5571
 5572    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5573    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5574
 5575    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5576        .unwrap();
 5577    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5578
 5579    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5580        .unwrap();
 5581    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5582}
 5583
 5584#[gpui::test]
 5585async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5586    init_test(cx, |_| {});
 5587
 5588    let language = Arc::new(Language::new(
 5589        LanguageConfig::default(),
 5590        Some(tree_sitter_rust::LANGUAGE.into()),
 5591    ));
 5592
 5593    let text = r#"
 5594        use mod1::mod2::{mod3, mod4};
 5595
 5596        fn fn_1(param1: bool, param2: &str) {
 5597            let var1 = "text";
 5598        }
 5599    "#
 5600    .unindent();
 5601
 5602    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5603    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5604    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5605
 5606    editor
 5607        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5608        .await;
 5609
 5610    editor.update_in(cx, |editor, window, cx| {
 5611        editor.change_selections(None, window, cx, |s| {
 5612            s.select_display_ranges([
 5613                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5614                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5615                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5616            ]);
 5617        });
 5618        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5619    });
 5620    editor.update(cx, |editor, cx| {
 5621        assert_text_with_selections(
 5622            editor,
 5623            indoc! {r#"
 5624                use mod1::mod2::{mod3, «mod4ˇ»};
 5625
 5626                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5627                    let var1 = "«textˇ»";
 5628                }
 5629            "#},
 5630            cx,
 5631        );
 5632    });
 5633
 5634    editor.update_in(cx, |editor, window, cx| {
 5635        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5636    });
 5637    editor.update(cx, |editor, cx| {
 5638        assert_text_with_selections(
 5639            editor,
 5640            indoc! {r#"
 5641                use mod1::mod2::«{mod3, mod4}ˇ»;
 5642
 5643                «ˇfn fn_1(param1: bool, param2: &str) {
 5644                    let var1 = "text";
 5645 5646            "#},
 5647            cx,
 5648        );
 5649    });
 5650
 5651    editor.update_in(cx, |editor, window, cx| {
 5652        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5653    });
 5654    assert_eq!(
 5655        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5656        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5657    );
 5658
 5659    // Trying to expand the selected syntax node one more time has no effect.
 5660    editor.update_in(cx, |editor, window, cx| {
 5661        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5662    });
 5663    assert_eq!(
 5664        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5665        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5666    );
 5667
 5668    editor.update_in(cx, |editor, window, cx| {
 5669        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5670    });
 5671    editor.update(cx, |editor, cx| {
 5672        assert_text_with_selections(
 5673            editor,
 5674            indoc! {r#"
 5675                use mod1::mod2::«{mod3, mod4}ˇ»;
 5676
 5677                «ˇfn fn_1(param1: bool, param2: &str) {
 5678                    let var1 = "text";
 5679 5680            "#},
 5681            cx,
 5682        );
 5683    });
 5684
 5685    editor.update_in(cx, |editor, window, cx| {
 5686        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5687    });
 5688    editor.update(cx, |editor, cx| {
 5689        assert_text_with_selections(
 5690            editor,
 5691            indoc! {r#"
 5692                use mod1::mod2::{mod3, «mod4ˇ»};
 5693
 5694                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5695                    let var1 = "«textˇ»";
 5696                }
 5697            "#},
 5698            cx,
 5699        );
 5700    });
 5701
 5702    editor.update_in(cx, |editor, window, cx| {
 5703        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5704    });
 5705    editor.update(cx, |editor, cx| {
 5706        assert_text_with_selections(
 5707            editor,
 5708            indoc! {r#"
 5709                use mod1::mod2::{mod3, mo«ˇ»d4};
 5710
 5711                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5712                    let var1 = "te«ˇ»xt";
 5713                }
 5714            "#},
 5715            cx,
 5716        );
 5717    });
 5718
 5719    // Trying to shrink the selected syntax node one more time has no effect.
 5720    editor.update_in(cx, |editor, window, cx| {
 5721        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5722    });
 5723    editor.update_in(cx, |editor, _, cx| {
 5724        assert_text_with_selections(
 5725            editor,
 5726            indoc! {r#"
 5727                use mod1::mod2::{mod3, mo«ˇ»d4};
 5728
 5729                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5730                    let var1 = "te«ˇ»xt";
 5731                }
 5732            "#},
 5733            cx,
 5734        );
 5735    });
 5736
 5737    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5738    // a fold.
 5739    editor.update_in(cx, |editor, window, cx| {
 5740        editor.fold_creases(
 5741            vec![
 5742                Crease::simple(
 5743                    Point::new(0, 21)..Point::new(0, 24),
 5744                    FoldPlaceholder::test(),
 5745                ),
 5746                Crease::simple(
 5747                    Point::new(3, 20)..Point::new(3, 22),
 5748                    FoldPlaceholder::test(),
 5749                ),
 5750            ],
 5751            true,
 5752            window,
 5753            cx,
 5754        );
 5755        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5756    });
 5757    editor.update(cx, |editor, cx| {
 5758        assert_text_with_selections(
 5759            editor,
 5760            indoc! {r#"
 5761                use mod1::mod2::«{mod3, mod4}ˇ»;
 5762
 5763                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5764                    «let var1 = "text";ˇ»
 5765                }
 5766            "#},
 5767            cx,
 5768        );
 5769    });
 5770}
 5771
 5772#[gpui::test]
 5773async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5774    init_test(cx, |_| {});
 5775
 5776    let base_text = r#"
 5777        impl A {
 5778            // this is an uncommitted comment
 5779
 5780            fn b() {
 5781                c();
 5782            }
 5783
 5784            // this is another uncommitted comment
 5785
 5786            fn d() {
 5787                // e
 5788                // f
 5789            }
 5790        }
 5791
 5792        fn g() {
 5793            // h
 5794        }
 5795    "#
 5796    .unindent();
 5797
 5798    let text = r#"
 5799        ˇimpl A {
 5800
 5801            fn b() {
 5802                c();
 5803            }
 5804
 5805            fn d() {
 5806                // e
 5807                // f
 5808            }
 5809        }
 5810
 5811        fn g() {
 5812            // h
 5813        }
 5814    "#
 5815    .unindent();
 5816
 5817    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5818    cx.set_state(&text);
 5819    cx.set_head_text(&base_text);
 5820    cx.update_editor(|editor, window, cx| {
 5821        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5822    });
 5823
 5824    cx.assert_state_with_diff(
 5825        "
 5826        ˇimpl A {
 5827      -     // this is an uncommitted comment
 5828
 5829            fn b() {
 5830                c();
 5831            }
 5832
 5833      -     // this is another uncommitted comment
 5834      -
 5835            fn d() {
 5836                // e
 5837                // f
 5838            }
 5839        }
 5840
 5841        fn g() {
 5842            // h
 5843        }
 5844    "
 5845        .unindent(),
 5846    );
 5847
 5848    let expected_display_text = "
 5849        impl A {
 5850            // this is an uncommitted comment
 5851
 5852            fn b() {
 5853 5854            }
 5855
 5856            // this is another uncommitted comment
 5857
 5858            fn d() {
 5859 5860            }
 5861        }
 5862
 5863        fn g() {
 5864 5865        }
 5866        "
 5867    .unindent();
 5868
 5869    cx.update_editor(|editor, window, cx| {
 5870        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5871        assert_eq!(editor.display_text(cx), expected_display_text);
 5872    });
 5873}
 5874
 5875#[gpui::test]
 5876async fn test_autoindent(cx: &mut TestAppContext) {
 5877    init_test(cx, |_| {});
 5878
 5879    let language = Arc::new(
 5880        Language::new(
 5881            LanguageConfig {
 5882                brackets: BracketPairConfig {
 5883                    pairs: vec![
 5884                        BracketPair {
 5885                            start: "{".to_string(),
 5886                            end: "}".to_string(),
 5887                            close: false,
 5888                            surround: false,
 5889                            newline: true,
 5890                        },
 5891                        BracketPair {
 5892                            start: "(".to_string(),
 5893                            end: ")".to_string(),
 5894                            close: false,
 5895                            surround: false,
 5896                            newline: true,
 5897                        },
 5898                    ],
 5899                    ..Default::default()
 5900                },
 5901                ..Default::default()
 5902            },
 5903            Some(tree_sitter_rust::LANGUAGE.into()),
 5904        )
 5905        .with_indents_query(
 5906            r#"
 5907                (_ "(" ")" @end) @indent
 5908                (_ "{" "}" @end) @indent
 5909            "#,
 5910        )
 5911        .unwrap(),
 5912    );
 5913
 5914    let text = "fn a() {}";
 5915
 5916    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5917    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5918    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5919    editor
 5920        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5921        .await;
 5922
 5923    editor.update_in(cx, |editor, window, cx| {
 5924        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5925        editor.newline(&Newline, window, cx);
 5926        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5927        assert_eq!(
 5928            editor.selections.ranges(cx),
 5929            &[
 5930                Point::new(1, 4)..Point::new(1, 4),
 5931                Point::new(3, 4)..Point::new(3, 4),
 5932                Point::new(5, 0)..Point::new(5, 0)
 5933            ]
 5934        );
 5935    });
 5936}
 5937
 5938#[gpui::test]
 5939async fn test_autoindent_selections(cx: &mut TestAppContext) {
 5940    init_test(cx, |_| {});
 5941
 5942    {
 5943        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5944        cx.set_state(indoc! {"
 5945            impl A {
 5946
 5947                fn b() {}
 5948
 5949            «fn c() {
 5950
 5951            }ˇ»
 5952            }
 5953        "});
 5954
 5955        cx.update_editor(|editor, window, cx| {
 5956            editor.autoindent(&Default::default(), window, cx);
 5957        });
 5958
 5959        cx.assert_editor_state(indoc! {"
 5960            impl A {
 5961
 5962                fn b() {}
 5963
 5964                «fn c() {
 5965
 5966                }ˇ»
 5967            }
 5968        "});
 5969    }
 5970
 5971    {
 5972        let mut cx = EditorTestContext::new_multibuffer(
 5973            cx,
 5974            [indoc! { "
 5975                impl A {
 5976                «
 5977                // a
 5978                fn b(){}
 5979                »
 5980                «
 5981                    }
 5982                    fn c(){}
 5983                »
 5984            "}],
 5985        );
 5986
 5987        let buffer = cx.update_editor(|editor, _, cx| {
 5988            let buffer = editor.buffer().update(cx, |buffer, _| {
 5989                buffer.all_buffers().iter().next().unwrap().clone()
 5990            });
 5991            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5992            buffer
 5993        });
 5994
 5995        cx.run_until_parked();
 5996        cx.update_editor(|editor, window, cx| {
 5997            editor.select_all(&Default::default(), window, cx);
 5998            editor.autoindent(&Default::default(), window, cx)
 5999        });
 6000        cx.run_until_parked();
 6001
 6002        cx.update(|_, cx| {
 6003            pretty_assertions::assert_eq!(
 6004                buffer.read(cx).text(),
 6005                indoc! { "
 6006                    impl A {
 6007
 6008                        // a
 6009                        fn b(){}
 6010
 6011
 6012                    }
 6013                    fn c(){}
 6014
 6015                " }
 6016            )
 6017        });
 6018    }
 6019}
 6020
 6021#[gpui::test]
 6022async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6023    init_test(cx, |_| {});
 6024
 6025    let mut cx = EditorTestContext::new(cx).await;
 6026
 6027    let language = Arc::new(Language::new(
 6028        LanguageConfig {
 6029            brackets: BracketPairConfig {
 6030                pairs: vec![
 6031                    BracketPair {
 6032                        start: "{".to_string(),
 6033                        end: "}".to_string(),
 6034                        close: true,
 6035                        surround: true,
 6036                        newline: true,
 6037                    },
 6038                    BracketPair {
 6039                        start: "(".to_string(),
 6040                        end: ")".to_string(),
 6041                        close: true,
 6042                        surround: true,
 6043                        newline: true,
 6044                    },
 6045                    BracketPair {
 6046                        start: "/*".to_string(),
 6047                        end: " */".to_string(),
 6048                        close: true,
 6049                        surround: true,
 6050                        newline: true,
 6051                    },
 6052                    BracketPair {
 6053                        start: "[".to_string(),
 6054                        end: "]".to_string(),
 6055                        close: false,
 6056                        surround: false,
 6057                        newline: true,
 6058                    },
 6059                    BracketPair {
 6060                        start: "\"".to_string(),
 6061                        end: "\"".to_string(),
 6062                        close: true,
 6063                        surround: true,
 6064                        newline: false,
 6065                    },
 6066                    BracketPair {
 6067                        start: "<".to_string(),
 6068                        end: ">".to_string(),
 6069                        close: false,
 6070                        surround: true,
 6071                        newline: true,
 6072                    },
 6073                ],
 6074                ..Default::default()
 6075            },
 6076            autoclose_before: "})]".to_string(),
 6077            ..Default::default()
 6078        },
 6079        Some(tree_sitter_rust::LANGUAGE.into()),
 6080    ));
 6081
 6082    cx.language_registry().add(language.clone());
 6083    cx.update_buffer(|buffer, cx| {
 6084        buffer.set_language(Some(language), cx);
 6085    });
 6086
 6087    cx.set_state(
 6088        &r#"
 6089            🏀ˇ
 6090            εˇ
 6091            ❤️ˇ
 6092        "#
 6093        .unindent(),
 6094    );
 6095
 6096    // autoclose multiple nested brackets at multiple cursors
 6097    cx.update_editor(|editor, window, cx| {
 6098        editor.handle_input("{", window, cx);
 6099        editor.handle_input("{", window, cx);
 6100        editor.handle_input("{", window, cx);
 6101    });
 6102    cx.assert_editor_state(
 6103        &"
 6104            🏀{{{ˇ}}}
 6105            ε{{{ˇ}}}
 6106            ❤️{{{ˇ}}}
 6107        "
 6108        .unindent(),
 6109    );
 6110
 6111    // insert a different closing bracket
 6112    cx.update_editor(|editor, window, cx| {
 6113        editor.handle_input(")", window, cx);
 6114    });
 6115    cx.assert_editor_state(
 6116        &"
 6117            🏀{{{)ˇ}}}
 6118            ε{{{)ˇ}}}
 6119            ❤️{{{)ˇ}}}
 6120        "
 6121        .unindent(),
 6122    );
 6123
 6124    // skip over the auto-closed brackets when typing a closing bracket
 6125    cx.update_editor(|editor, window, cx| {
 6126        editor.move_right(&MoveRight, window, cx);
 6127        editor.handle_input("}", window, cx);
 6128        editor.handle_input("}", window, cx);
 6129        editor.handle_input("}", window, cx);
 6130    });
 6131    cx.assert_editor_state(
 6132        &"
 6133            🏀{{{)}}}}ˇ
 6134            ε{{{)}}}}ˇ
 6135            ❤️{{{)}}}}ˇ
 6136        "
 6137        .unindent(),
 6138    );
 6139
 6140    // autoclose multi-character pairs
 6141    cx.set_state(
 6142        &"
 6143            ˇ
 6144            ˇ
 6145        "
 6146        .unindent(),
 6147    );
 6148    cx.update_editor(|editor, window, cx| {
 6149        editor.handle_input("/", window, cx);
 6150        editor.handle_input("*", window, cx);
 6151    });
 6152    cx.assert_editor_state(
 6153        &"
 6154            /*ˇ */
 6155            /*ˇ */
 6156        "
 6157        .unindent(),
 6158    );
 6159
 6160    // one cursor autocloses a multi-character pair, one cursor
 6161    // does not autoclose.
 6162    cx.set_state(
 6163        &"
 6164 6165            ˇ
 6166        "
 6167        .unindent(),
 6168    );
 6169    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6170    cx.assert_editor_state(
 6171        &"
 6172            /*ˇ */
 6173 6174        "
 6175        .unindent(),
 6176    );
 6177
 6178    // Don't autoclose if the next character isn't whitespace and isn't
 6179    // listed in the language's "autoclose_before" section.
 6180    cx.set_state("ˇa b");
 6181    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6182    cx.assert_editor_state("{ˇa b");
 6183
 6184    // Don't autoclose if `close` is false for the bracket pair
 6185    cx.set_state("ˇ");
 6186    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6187    cx.assert_editor_state("");
 6188
 6189    // Surround with brackets if text is selected
 6190    cx.set_state("«aˇ» b");
 6191    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6192    cx.assert_editor_state("{«aˇ»} b");
 6193
 6194    // Autclose pair where the start and end characters are the same
 6195    cx.set_state("");
 6196    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6197    cx.assert_editor_state("a\"ˇ\"");
 6198    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6199    cx.assert_editor_state("a\"\"ˇ");
 6200
 6201    // Don't autoclose pair if autoclose is disabled
 6202    cx.set_state("ˇ");
 6203    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6204    cx.assert_editor_state("");
 6205
 6206    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6207    cx.set_state("«aˇ» b");
 6208    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6209    cx.assert_editor_state("<«aˇ»> b");
 6210}
 6211
 6212#[gpui::test]
 6213async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6214    init_test(cx, |settings| {
 6215        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6216    });
 6217
 6218    let mut cx = EditorTestContext::new(cx).await;
 6219
 6220    let language = Arc::new(Language::new(
 6221        LanguageConfig {
 6222            brackets: BracketPairConfig {
 6223                pairs: vec![
 6224                    BracketPair {
 6225                        start: "{".to_string(),
 6226                        end: "}".to_string(),
 6227                        close: true,
 6228                        surround: true,
 6229                        newline: true,
 6230                    },
 6231                    BracketPair {
 6232                        start: "(".to_string(),
 6233                        end: ")".to_string(),
 6234                        close: true,
 6235                        surround: true,
 6236                        newline: true,
 6237                    },
 6238                    BracketPair {
 6239                        start: "[".to_string(),
 6240                        end: "]".to_string(),
 6241                        close: false,
 6242                        surround: false,
 6243                        newline: true,
 6244                    },
 6245                ],
 6246                ..Default::default()
 6247            },
 6248            autoclose_before: "})]".to_string(),
 6249            ..Default::default()
 6250        },
 6251        Some(tree_sitter_rust::LANGUAGE.into()),
 6252    ));
 6253
 6254    cx.language_registry().add(language.clone());
 6255    cx.update_buffer(|buffer, cx| {
 6256        buffer.set_language(Some(language), cx);
 6257    });
 6258
 6259    cx.set_state(
 6260        &"
 6261            ˇ
 6262            ˇ
 6263            ˇ
 6264        "
 6265        .unindent(),
 6266    );
 6267
 6268    // ensure only matching closing brackets are skipped over
 6269    cx.update_editor(|editor, window, cx| {
 6270        editor.handle_input("}", window, cx);
 6271        editor.move_left(&MoveLeft, window, cx);
 6272        editor.handle_input(")", window, cx);
 6273        editor.move_left(&MoveLeft, window, cx);
 6274    });
 6275    cx.assert_editor_state(
 6276        &"
 6277            ˇ)}
 6278            ˇ)}
 6279            ˇ)}
 6280        "
 6281        .unindent(),
 6282    );
 6283
 6284    // skip-over closing brackets at multiple cursors
 6285    cx.update_editor(|editor, window, cx| {
 6286        editor.handle_input(")", window, cx);
 6287        editor.handle_input("}", window, cx);
 6288    });
 6289    cx.assert_editor_state(
 6290        &"
 6291            )}ˇ
 6292            )}ˇ
 6293            )}ˇ
 6294        "
 6295        .unindent(),
 6296    );
 6297
 6298    // ignore non-close brackets
 6299    cx.update_editor(|editor, window, cx| {
 6300        editor.handle_input("]", window, cx);
 6301        editor.move_left(&MoveLeft, window, cx);
 6302        editor.handle_input("]", window, cx);
 6303    });
 6304    cx.assert_editor_state(
 6305        &"
 6306            )}]ˇ]
 6307            )}]ˇ]
 6308            )}]ˇ]
 6309        "
 6310        .unindent(),
 6311    );
 6312}
 6313
 6314#[gpui::test]
 6315async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6316    init_test(cx, |_| {});
 6317
 6318    let mut cx = EditorTestContext::new(cx).await;
 6319
 6320    let html_language = Arc::new(
 6321        Language::new(
 6322            LanguageConfig {
 6323                name: "HTML".into(),
 6324                brackets: BracketPairConfig {
 6325                    pairs: vec![
 6326                        BracketPair {
 6327                            start: "<".into(),
 6328                            end: ">".into(),
 6329                            close: true,
 6330                            ..Default::default()
 6331                        },
 6332                        BracketPair {
 6333                            start: "{".into(),
 6334                            end: "}".into(),
 6335                            close: true,
 6336                            ..Default::default()
 6337                        },
 6338                        BracketPair {
 6339                            start: "(".into(),
 6340                            end: ")".into(),
 6341                            close: true,
 6342                            ..Default::default()
 6343                        },
 6344                    ],
 6345                    ..Default::default()
 6346                },
 6347                autoclose_before: "})]>".into(),
 6348                ..Default::default()
 6349            },
 6350            Some(tree_sitter_html::LANGUAGE.into()),
 6351        )
 6352        .with_injection_query(
 6353            r#"
 6354            (script_element
 6355                (raw_text) @injection.content
 6356                (#set! injection.language "javascript"))
 6357            "#,
 6358        )
 6359        .unwrap(),
 6360    );
 6361
 6362    let javascript_language = Arc::new(Language::new(
 6363        LanguageConfig {
 6364            name: "JavaScript".into(),
 6365            brackets: BracketPairConfig {
 6366                pairs: vec![
 6367                    BracketPair {
 6368                        start: "/*".into(),
 6369                        end: " */".into(),
 6370                        close: true,
 6371                        ..Default::default()
 6372                    },
 6373                    BracketPair {
 6374                        start: "{".into(),
 6375                        end: "}".into(),
 6376                        close: true,
 6377                        ..Default::default()
 6378                    },
 6379                    BracketPair {
 6380                        start: "(".into(),
 6381                        end: ")".into(),
 6382                        close: true,
 6383                        ..Default::default()
 6384                    },
 6385                ],
 6386                ..Default::default()
 6387            },
 6388            autoclose_before: "})]>".into(),
 6389            ..Default::default()
 6390        },
 6391        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6392    ));
 6393
 6394    cx.language_registry().add(html_language.clone());
 6395    cx.language_registry().add(javascript_language.clone());
 6396
 6397    cx.update_buffer(|buffer, cx| {
 6398        buffer.set_language(Some(html_language), cx);
 6399    });
 6400
 6401    cx.set_state(
 6402        &r#"
 6403            <body>ˇ
 6404                <script>
 6405                    var x = 1;ˇ
 6406                </script>
 6407            </body>ˇ
 6408        "#
 6409        .unindent(),
 6410    );
 6411
 6412    // Precondition: different languages are active at different locations.
 6413    cx.update_editor(|editor, window, cx| {
 6414        let snapshot = editor.snapshot(window, cx);
 6415        let cursors = editor.selections.ranges::<usize>(cx);
 6416        let languages = cursors
 6417            .iter()
 6418            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6419            .collect::<Vec<_>>();
 6420        assert_eq!(
 6421            languages,
 6422            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6423        );
 6424    });
 6425
 6426    // Angle brackets autoclose in HTML, but not JavaScript.
 6427    cx.update_editor(|editor, window, cx| {
 6428        editor.handle_input("<", window, cx);
 6429        editor.handle_input("a", window, cx);
 6430    });
 6431    cx.assert_editor_state(
 6432        &r#"
 6433            <body><aˇ>
 6434                <script>
 6435                    var x = 1;<aˇ
 6436                </script>
 6437            </body><aˇ>
 6438        "#
 6439        .unindent(),
 6440    );
 6441
 6442    // Curly braces and parens autoclose in both HTML and JavaScript.
 6443    cx.update_editor(|editor, window, cx| {
 6444        editor.handle_input(" b=", window, cx);
 6445        editor.handle_input("{", window, cx);
 6446        editor.handle_input("c", window, cx);
 6447        editor.handle_input("(", window, cx);
 6448    });
 6449    cx.assert_editor_state(
 6450        &r#"
 6451            <body><a b={c(ˇ)}>
 6452                <script>
 6453                    var x = 1;<a b={c(ˇ)}
 6454                </script>
 6455            </body><a b={c(ˇ)}>
 6456        "#
 6457        .unindent(),
 6458    );
 6459
 6460    // Brackets that were already autoclosed are skipped.
 6461    cx.update_editor(|editor, window, cx| {
 6462        editor.handle_input(")", window, cx);
 6463        editor.handle_input("d", window, cx);
 6464        editor.handle_input("}", window, cx);
 6465    });
 6466    cx.assert_editor_state(
 6467        &r#"
 6468            <body><a b={c()d}ˇ>
 6469                <script>
 6470                    var x = 1;<a b={c()d}ˇ
 6471                </script>
 6472            </body><a b={c()d}ˇ>
 6473        "#
 6474        .unindent(),
 6475    );
 6476    cx.update_editor(|editor, window, cx| {
 6477        editor.handle_input(">", window, cx);
 6478    });
 6479    cx.assert_editor_state(
 6480        &r#"
 6481            <body><a b={c()d}>ˇ
 6482                <script>
 6483                    var x = 1;<a b={c()d}>ˇ
 6484                </script>
 6485            </body><a b={c()d}>ˇ
 6486        "#
 6487        .unindent(),
 6488    );
 6489
 6490    // Reset
 6491    cx.set_state(
 6492        &r#"
 6493            <body>ˇ
 6494                <script>
 6495                    var x = 1;ˇ
 6496                </script>
 6497            </body>ˇ
 6498        "#
 6499        .unindent(),
 6500    );
 6501
 6502    cx.update_editor(|editor, window, cx| {
 6503        editor.handle_input("<", window, cx);
 6504    });
 6505    cx.assert_editor_state(
 6506        &r#"
 6507            <body><ˇ>
 6508                <script>
 6509                    var x = 1;<ˇ
 6510                </script>
 6511            </body><ˇ>
 6512        "#
 6513        .unindent(),
 6514    );
 6515
 6516    // When backspacing, the closing angle brackets are removed.
 6517    cx.update_editor(|editor, window, cx| {
 6518        editor.backspace(&Backspace, window, cx);
 6519    });
 6520    cx.assert_editor_state(
 6521        &r#"
 6522            <body>ˇ
 6523                <script>
 6524                    var x = 1;ˇ
 6525                </script>
 6526            </body>ˇ
 6527        "#
 6528        .unindent(),
 6529    );
 6530
 6531    // Block comments autoclose in JavaScript, but not HTML.
 6532    cx.update_editor(|editor, window, cx| {
 6533        editor.handle_input("/", window, cx);
 6534        editor.handle_input("*", window, cx);
 6535    });
 6536    cx.assert_editor_state(
 6537        &r#"
 6538            <body>/*ˇ
 6539                <script>
 6540                    var x = 1;/*ˇ */
 6541                </script>
 6542            </body>/*ˇ
 6543        "#
 6544        .unindent(),
 6545    );
 6546}
 6547
 6548#[gpui::test]
 6549async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6550    init_test(cx, |_| {});
 6551
 6552    let mut cx = EditorTestContext::new(cx).await;
 6553
 6554    let rust_language = Arc::new(
 6555        Language::new(
 6556            LanguageConfig {
 6557                name: "Rust".into(),
 6558                brackets: serde_json::from_value(json!([
 6559                    { "start": "{", "end": "}", "close": true, "newline": true },
 6560                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6561                ]))
 6562                .unwrap(),
 6563                autoclose_before: "})]>".into(),
 6564                ..Default::default()
 6565            },
 6566            Some(tree_sitter_rust::LANGUAGE.into()),
 6567        )
 6568        .with_override_query("(string_literal) @string")
 6569        .unwrap(),
 6570    );
 6571
 6572    cx.language_registry().add(rust_language.clone());
 6573    cx.update_buffer(|buffer, cx| {
 6574        buffer.set_language(Some(rust_language), cx);
 6575    });
 6576
 6577    cx.set_state(
 6578        &r#"
 6579            let x = ˇ
 6580        "#
 6581        .unindent(),
 6582    );
 6583
 6584    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6585    cx.update_editor(|editor, window, cx| {
 6586        editor.handle_input("\"", window, cx);
 6587    });
 6588    cx.assert_editor_state(
 6589        &r#"
 6590            let x = "ˇ"
 6591        "#
 6592        .unindent(),
 6593    );
 6594
 6595    // Inserting another quotation mark. The cursor moves across the existing
 6596    // automatically-inserted quotation mark.
 6597    cx.update_editor(|editor, window, cx| {
 6598        editor.handle_input("\"", window, cx);
 6599    });
 6600    cx.assert_editor_state(
 6601        &r#"
 6602            let x = ""ˇ
 6603        "#
 6604        .unindent(),
 6605    );
 6606
 6607    // Reset
 6608    cx.set_state(
 6609        &r#"
 6610            let x = ˇ
 6611        "#
 6612        .unindent(),
 6613    );
 6614
 6615    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6616    cx.update_editor(|editor, window, cx| {
 6617        editor.handle_input("\"", window, cx);
 6618        editor.handle_input(" ", window, cx);
 6619        editor.move_left(&Default::default(), window, cx);
 6620        editor.handle_input("\\", window, cx);
 6621        editor.handle_input("\"", window, cx);
 6622    });
 6623    cx.assert_editor_state(
 6624        &r#"
 6625            let x = "\"ˇ "
 6626        "#
 6627        .unindent(),
 6628    );
 6629
 6630    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6631    // mark. Nothing is inserted.
 6632    cx.update_editor(|editor, window, cx| {
 6633        editor.move_right(&Default::default(), window, cx);
 6634        editor.handle_input("\"", window, cx);
 6635    });
 6636    cx.assert_editor_state(
 6637        &r#"
 6638            let x = "\" "ˇ
 6639        "#
 6640        .unindent(),
 6641    );
 6642}
 6643
 6644#[gpui::test]
 6645async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6646    init_test(cx, |_| {});
 6647
 6648    let language = Arc::new(Language::new(
 6649        LanguageConfig {
 6650            brackets: BracketPairConfig {
 6651                pairs: vec![
 6652                    BracketPair {
 6653                        start: "{".to_string(),
 6654                        end: "}".to_string(),
 6655                        close: true,
 6656                        surround: true,
 6657                        newline: true,
 6658                    },
 6659                    BracketPair {
 6660                        start: "/* ".to_string(),
 6661                        end: "*/".to_string(),
 6662                        close: true,
 6663                        surround: true,
 6664                        ..Default::default()
 6665                    },
 6666                ],
 6667                ..Default::default()
 6668            },
 6669            ..Default::default()
 6670        },
 6671        Some(tree_sitter_rust::LANGUAGE.into()),
 6672    ));
 6673
 6674    let text = r#"
 6675        a
 6676        b
 6677        c
 6678    "#
 6679    .unindent();
 6680
 6681    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6682    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6683    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6684    editor
 6685        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6686        .await;
 6687
 6688    editor.update_in(cx, |editor, window, cx| {
 6689        editor.change_selections(None, window, cx, |s| {
 6690            s.select_display_ranges([
 6691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6693                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6694            ])
 6695        });
 6696
 6697        editor.handle_input("{", window, cx);
 6698        editor.handle_input("{", window, cx);
 6699        editor.handle_input("{", window, cx);
 6700        assert_eq!(
 6701            editor.text(cx),
 6702            "
 6703                {{{a}}}
 6704                {{{b}}}
 6705                {{{c}}}
 6706            "
 6707            .unindent()
 6708        );
 6709        assert_eq!(
 6710            editor.selections.display_ranges(cx),
 6711            [
 6712                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6713                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6714                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6715            ]
 6716        );
 6717
 6718        editor.undo(&Undo, window, cx);
 6719        editor.undo(&Undo, window, cx);
 6720        editor.undo(&Undo, window, cx);
 6721        assert_eq!(
 6722            editor.text(cx),
 6723            "
 6724                a
 6725                b
 6726                c
 6727            "
 6728            .unindent()
 6729        );
 6730        assert_eq!(
 6731            editor.selections.display_ranges(cx),
 6732            [
 6733                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6734                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6735                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6736            ]
 6737        );
 6738
 6739        // Ensure inserting the first character of a multi-byte bracket pair
 6740        // doesn't surround the selections with the bracket.
 6741        editor.handle_input("/", window, cx);
 6742        assert_eq!(
 6743            editor.text(cx),
 6744            "
 6745                /
 6746                /
 6747                /
 6748            "
 6749            .unindent()
 6750        );
 6751        assert_eq!(
 6752            editor.selections.display_ranges(cx),
 6753            [
 6754                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6755                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6756                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6757            ]
 6758        );
 6759
 6760        editor.undo(&Undo, window, cx);
 6761        assert_eq!(
 6762            editor.text(cx),
 6763            "
 6764                a
 6765                b
 6766                c
 6767            "
 6768            .unindent()
 6769        );
 6770        assert_eq!(
 6771            editor.selections.display_ranges(cx),
 6772            [
 6773                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6774                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6775                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6776            ]
 6777        );
 6778
 6779        // Ensure inserting the last character of a multi-byte bracket pair
 6780        // doesn't surround the selections with the bracket.
 6781        editor.handle_input("*", window, cx);
 6782        assert_eq!(
 6783            editor.text(cx),
 6784            "
 6785                *
 6786                *
 6787                *
 6788            "
 6789            .unindent()
 6790        );
 6791        assert_eq!(
 6792            editor.selections.display_ranges(cx),
 6793            [
 6794                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6795                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6796                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6797            ]
 6798        );
 6799    });
 6800}
 6801
 6802#[gpui::test]
 6803async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6804    init_test(cx, |_| {});
 6805
 6806    let language = Arc::new(Language::new(
 6807        LanguageConfig {
 6808            brackets: BracketPairConfig {
 6809                pairs: vec![BracketPair {
 6810                    start: "{".to_string(),
 6811                    end: "}".to_string(),
 6812                    close: true,
 6813                    surround: true,
 6814                    newline: true,
 6815                }],
 6816                ..Default::default()
 6817            },
 6818            autoclose_before: "}".to_string(),
 6819            ..Default::default()
 6820        },
 6821        Some(tree_sitter_rust::LANGUAGE.into()),
 6822    ));
 6823
 6824    let text = r#"
 6825        a
 6826        b
 6827        c
 6828    "#
 6829    .unindent();
 6830
 6831    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6832    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6833    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6834    editor
 6835        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6836        .await;
 6837
 6838    editor.update_in(cx, |editor, window, cx| {
 6839        editor.change_selections(None, window, cx, |s| {
 6840            s.select_ranges([
 6841                Point::new(0, 1)..Point::new(0, 1),
 6842                Point::new(1, 1)..Point::new(1, 1),
 6843                Point::new(2, 1)..Point::new(2, 1),
 6844            ])
 6845        });
 6846
 6847        editor.handle_input("{", window, cx);
 6848        editor.handle_input("{", window, cx);
 6849        editor.handle_input("_", window, cx);
 6850        assert_eq!(
 6851            editor.text(cx),
 6852            "
 6853                a{{_}}
 6854                b{{_}}
 6855                c{{_}}
 6856            "
 6857            .unindent()
 6858        );
 6859        assert_eq!(
 6860            editor.selections.ranges::<Point>(cx),
 6861            [
 6862                Point::new(0, 4)..Point::new(0, 4),
 6863                Point::new(1, 4)..Point::new(1, 4),
 6864                Point::new(2, 4)..Point::new(2, 4)
 6865            ]
 6866        );
 6867
 6868        editor.backspace(&Default::default(), window, cx);
 6869        editor.backspace(&Default::default(), window, cx);
 6870        assert_eq!(
 6871            editor.text(cx),
 6872            "
 6873                a{}
 6874                b{}
 6875                c{}
 6876            "
 6877            .unindent()
 6878        );
 6879        assert_eq!(
 6880            editor.selections.ranges::<Point>(cx),
 6881            [
 6882                Point::new(0, 2)..Point::new(0, 2),
 6883                Point::new(1, 2)..Point::new(1, 2),
 6884                Point::new(2, 2)..Point::new(2, 2)
 6885            ]
 6886        );
 6887
 6888        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6889        assert_eq!(
 6890            editor.text(cx),
 6891            "
 6892                a
 6893                b
 6894                c
 6895            "
 6896            .unindent()
 6897        );
 6898        assert_eq!(
 6899            editor.selections.ranges::<Point>(cx),
 6900            [
 6901                Point::new(0, 1)..Point::new(0, 1),
 6902                Point::new(1, 1)..Point::new(1, 1),
 6903                Point::new(2, 1)..Point::new(2, 1)
 6904            ]
 6905        );
 6906    });
 6907}
 6908
 6909#[gpui::test]
 6910async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 6911    init_test(cx, |settings| {
 6912        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6913    });
 6914
 6915    let mut cx = EditorTestContext::new(cx).await;
 6916
 6917    let language = Arc::new(Language::new(
 6918        LanguageConfig {
 6919            brackets: BracketPairConfig {
 6920                pairs: vec![
 6921                    BracketPair {
 6922                        start: "{".to_string(),
 6923                        end: "}".to_string(),
 6924                        close: true,
 6925                        surround: true,
 6926                        newline: true,
 6927                    },
 6928                    BracketPair {
 6929                        start: "(".to_string(),
 6930                        end: ")".to_string(),
 6931                        close: true,
 6932                        surround: true,
 6933                        newline: true,
 6934                    },
 6935                    BracketPair {
 6936                        start: "[".to_string(),
 6937                        end: "]".to_string(),
 6938                        close: false,
 6939                        surround: true,
 6940                        newline: true,
 6941                    },
 6942                ],
 6943                ..Default::default()
 6944            },
 6945            autoclose_before: "})]".to_string(),
 6946            ..Default::default()
 6947        },
 6948        Some(tree_sitter_rust::LANGUAGE.into()),
 6949    ));
 6950
 6951    cx.language_registry().add(language.clone());
 6952    cx.update_buffer(|buffer, cx| {
 6953        buffer.set_language(Some(language), cx);
 6954    });
 6955
 6956    cx.set_state(
 6957        &"
 6958            {(ˇ)}
 6959            [[ˇ]]
 6960            {(ˇ)}
 6961        "
 6962        .unindent(),
 6963    );
 6964
 6965    cx.update_editor(|editor, window, cx| {
 6966        editor.backspace(&Default::default(), window, cx);
 6967        editor.backspace(&Default::default(), window, cx);
 6968    });
 6969
 6970    cx.assert_editor_state(
 6971        &"
 6972            ˇ
 6973            ˇ]]
 6974            ˇ
 6975        "
 6976        .unindent(),
 6977    );
 6978
 6979    cx.update_editor(|editor, window, cx| {
 6980        editor.handle_input("{", window, cx);
 6981        editor.handle_input("{", window, cx);
 6982        editor.move_right(&MoveRight, window, cx);
 6983        editor.move_right(&MoveRight, window, cx);
 6984        editor.move_left(&MoveLeft, window, cx);
 6985        editor.move_left(&MoveLeft, window, cx);
 6986        editor.backspace(&Default::default(), window, cx);
 6987    });
 6988
 6989    cx.assert_editor_state(
 6990        &"
 6991            {ˇ}
 6992            {ˇ}]]
 6993            {ˇ}
 6994        "
 6995        .unindent(),
 6996    );
 6997
 6998    cx.update_editor(|editor, window, cx| {
 6999        editor.backspace(&Default::default(), window, cx);
 7000    });
 7001
 7002    cx.assert_editor_state(
 7003        &"
 7004            ˇ
 7005            ˇ]]
 7006            ˇ
 7007        "
 7008        .unindent(),
 7009    );
 7010}
 7011
 7012#[gpui::test]
 7013async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7014    init_test(cx, |_| {});
 7015
 7016    let language = Arc::new(Language::new(
 7017        LanguageConfig::default(),
 7018        Some(tree_sitter_rust::LANGUAGE.into()),
 7019    ));
 7020
 7021    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7022    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7023    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7024    editor
 7025        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7026        .await;
 7027
 7028    editor.update_in(cx, |editor, window, cx| {
 7029        editor.set_auto_replace_emoji_shortcode(true);
 7030
 7031        editor.handle_input("Hello ", window, cx);
 7032        editor.handle_input(":wave", window, cx);
 7033        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7034
 7035        editor.handle_input(":", window, cx);
 7036        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7037
 7038        editor.handle_input(" :smile", window, cx);
 7039        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7040
 7041        editor.handle_input(":", window, cx);
 7042        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7043
 7044        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7045        editor.handle_input(":wave", window, cx);
 7046        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7047
 7048        editor.handle_input(":", window, cx);
 7049        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7050
 7051        editor.handle_input(":1", window, cx);
 7052        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7053
 7054        editor.handle_input(":", window, cx);
 7055        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7056
 7057        // Ensure shortcode does not get replaced when it is part of a word
 7058        editor.handle_input(" Test:wave", window, cx);
 7059        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7060
 7061        editor.handle_input(":", window, cx);
 7062        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7063
 7064        editor.set_auto_replace_emoji_shortcode(false);
 7065
 7066        // Ensure shortcode does not get replaced when auto replace is off
 7067        editor.handle_input(" :wave", window, cx);
 7068        assert_eq!(
 7069            editor.text(cx),
 7070            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7071        );
 7072
 7073        editor.handle_input(":", window, cx);
 7074        assert_eq!(
 7075            editor.text(cx),
 7076            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7077        );
 7078    });
 7079}
 7080
 7081#[gpui::test]
 7082async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7083    init_test(cx, |_| {});
 7084
 7085    let (text, insertion_ranges) = marked_text_ranges(
 7086        indoc! {"
 7087            ˇ
 7088        "},
 7089        false,
 7090    );
 7091
 7092    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7093    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7094
 7095    _ = editor.update_in(cx, |editor, window, cx| {
 7096        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7097
 7098        editor
 7099            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7100            .unwrap();
 7101
 7102        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7103            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7104            assert_eq!(editor.text(cx), expected_text);
 7105            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7106        }
 7107
 7108        assert(
 7109            editor,
 7110            cx,
 7111            indoc! {"
 7112            type «» =•
 7113            "},
 7114        );
 7115
 7116        assert!(editor.context_menu_visible(), "There should be a matches");
 7117    });
 7118}
 7119
 7120#[gpui::test]
 7121async fn test_snippets(cx: &mut TestAppContext) {
 7122    init_test(cx, |_| {});
 7123
 7124    let (text, insertion_ranges) = marked_text_ranges(
 7125        indoc! {"
 7126            a.ˇ b
 7127            a.ˇ b
 7128            a.ˇ b
 7129        "},
 7130        false,
 7131    );
 7132
 7133    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7134    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7135
 7136    editor.update_in(cx, |editor, window, cx| {
 7137        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7138
 7139        editor
 7140            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7141            .unwrap();
 7142
 7143        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7144            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7145            assert_eq!(editor.text(cx), expected_text);
 7146            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7147        }
 7148
 7149        assert(
 7150            editor,
 7151            cx,
 7152            indoc! {"
 7153                a.f(«one», two, «three») b
 7154                a.f(«one», two, «three») b
 7155                a.f(«one», two, «three») b
 7156            "},
 7157        );
 7158
 7159        // Can't move earlier than the first tab stop
 7160        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7161        assert(
 7162            editor,
 7163            cx,
 7164            indoc! {"
 7165                a.f(«one», two, «three») b
 7166                a.f(«one», two, «three») b
 7167                a.f(«one», two, «three») b
 7168            "},
 7169        );
 7170
 7171        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7172        assert(
 7173            editor,
 7174            cx,
 7175            indoc! {"
 7176                a.f(one, «two», three) b
 7177                a.f(one, «two», three) b
 7178                a.f(one, «two», three) b
 7179            "},
 7180        );
 7181
 7182        editor.move_to_prev_snippet_tabstop(window, cx);
 7183        assert(
 7184            editor,
 7185            cx,
 7186            indoc! {"
 7187                a.f(«one», two, «three») b
 7188                a.f(«one», two, «three») b
 7189                a.f(«one», two, «three») b
 7190            "},
 7191        );
 7192
 7193        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7194        assert(
 7195            editor,
 7196            cx,
 7197            indoc! {"
 7198                a.f(one, «two», three) b
 7199                a.f(one, «two», three) b
 7200                a.f(one, «two», three) b
 7201            "},
 7202        );
 7203        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7204        assert(
 7205            editor,
 7206            cx,
 7207            indoc! {"
 7208                a.f(one, two, three)ˇ b
 7209                a.f(one, two, three)ˇ b
 7210                a.f(one, two, three)ˇ b
 7211            "},
 7212        );
 7213
 7214        // As soon as the last tab stop is reached, snippet state is gone
 7215        editor.move_to_prev_snippet_tabstop(window, cx);
 7216        assert(
 7217            editor,
 7218            cx,
 7219            indoc! {"
 7220                a.f(one, two, three)ˇ b
 7221                a.f(one, two, three)ˇ b
 7222                a.f(one, two, three)ˇ b
 7223            "},
 7224        );
 7225    });
 7226}
 7227
 7228#[gpui::test]
 7229async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7230    init_test(cx, |_| {});
 7231
 7232    let fs = FakeFs::new(cx.executor());
 7233    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7234
 7235    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7236
 7237    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7238    language_registry.add(rust_lang());
 7239    let mut fake_servers = language_registry.register_fake_lsp(
 7240        "Rust",
 7241        FakeLspAdapter {
 7242            capabilities: lsp::ServerCapabilities {
 7243                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7244                ..Default::default()
 7245            },
 7246            ..Default::default()
 7247        },
 7248    );
 7249
 7250    let buffer = project
 7251        .update(cx, |project, cx| {
 7252            project.open_local_buffer(path!("/file.rs"), cx)
 7253        })
 7254        .await
 7255        .unwrap();
 7256
 7257    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7258    let (editor, cx) = cx.add_window_view(|window, cx| {
 7259        build_editor_with_project(project.clone(), buffer, window, cx)
 7260    });
 7261    editor.update_in(cx, |editor, window, cx| {
 7262        editor.set_text("one\ntwo\nthree\n", window, cx)
 7263    });
 7264    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7265
 7266    cx.executor().start_waiting();
 7267    let fake_server = fake_servers.next().await.unwrap();
 7268
 7269    let save = editor
 7270        .update_in(cx, |editor, window, cx| {
 7271            editor.save(true, project.clone(), window, cx)
 7272        })
 7273        .unwrap();
 7274    fake_server
 7275        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7276            assert_eq!(
 7277                params.text_document.uri,
 7278                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7279            );
 7280            assert_eq!(params.options.tab_size, 4);
 7281            Ok(Some(vec![lsp::TextEdit::new(
 7282                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7283                ", ".to_string(),
 7284            )]))
 7285        })
 7286        .next()
 7287        .await;
 7288    cx.executor().start_waiting();
 7289    save.await;
 7290
 7291    assert_eq!(
 7292        editor.update(cx, |editor, cx| editor.text(cx)),
 7293        "one, two\nthree\n"
 7294    );
 7295    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7296
 7297    editor.update_in(cx, |editor, window, cx| {
 7298        editor.set_text("one\ntwo\nthree\n", window, cx)
 7299    });
 7300    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7301
 7302    // Ensure we can still save even if formatting hangs.
 7303    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7304        assert_eq!(
 7305            params.text_document.uri,
 7306            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7307        );
 7308        futures::future::pending::<()>().await;
 7309        unreachable!()
 7310    });
 7311    let save = editor
 7312        .update_in(cx, |editor, window, cx| {
 7313            editor.save(true, project.clone(), window, cx)
 7314        })
 7315        .unwrap();
 7316    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7317    cx.executor().start_waiting();
 7318    save.await;
 7319    assert_eq!(
 7320        editor.update(cx, |editor, cx| editor.text(cx)),
 7321        "one\ntwo\nthree\n"
 7322    );
 7323    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7324
 7325    // For non-dirty buffer, no formatting request should be sent
 7326    let save = editor
 7327        .update_in(cx, |editor, window, cx| {
 7328            editor.save(true, project.clone(), window, cx)
 7329        })
 7330        .unwrap();
 7331    let _pending_format_request = fake_server
 7332        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7333            panic!("Should not be invoked on non-dirty buffer");
 7334        })
 7335        .next();
 7336    cx.executor().start_waiting();
 7337    save.await;
 7338
 7339    // Set rust language override and assert overridden tabsize is sent to language server
 7340    update_test_language_settings(cx, |settings| {
 7341        settings.languages.insert(
 7342            "Rust".into(),
 7343            LanguageSettingsContent {
 7344                tab_size: NonZeroU32::new(8),
 7345                ..Default::default()
 7346            },
 7347        );
 7348    });
 7349
 7350    editor.update_in(cx, |editor, window, cx| {
 7351        editor.set_text("somehting_new\n", window, cx)
 7352    });
 7353    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7354    let save = editor
 7355        .update_in(cx, |editor, window, cx| {
 7356            editor.save(true, project.clone(), window, cx)
 7357        })
 7358        .unwrap();
 7359    fake_server
 7360        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7361            assert_eq!(
 7362                params.text_document.uri,
 7363                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7364            );
 7365            assert_eq!(params.options.tab_size, 8);
 7366            Ok(Some(vec![]))
 7367        })
 7368        .next()
 7369        .await;
 7370    cx.executor().start_waiting();
 7371    save.await;
 7372}
 7373
 7374#[gpui::test]
 7375async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7376    init_test(cx, |_| {});
 7377
 7378    let cols = 4;
 7379    let rows = 10;
 7380    let sample_text_1 = sample_text(rows, cols, 'a');
 7381    assert_eq!(
 7382        sample_text_1,
 7383        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7384    );
 7385    let sample_text_2 = sample_text(rows, cols, 'l');
 7386    assert_eq!(
 7387        sample_text_2,
 7388        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7389    );
 7390    let sample_text_3 = sample_text(rows, cols, 'v');
 7391    assert_eq!(
 7392        sample_text_3,
 7393        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7394    );
 7395
 7396    let fs = FakeFs::new(cx.executor());
 7397    fs.insert_tree(
 7398        path!("/a"),
 7399        json!({
 7400            "main.rs": sample_text_1,
 7401            "other.rs": sample_text_2,
 7402            "lib.rs": sample_text_3,
 7403        }),
 7404    )
 7405    .await;
 7406
 7407    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7408    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7409    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7410
 7411    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7412    language_registry.add(rust_lang());
 7413    let mut fake_servers = language_registry.register_fake_lsp(
 7414        "Rust",
 7415        FakeLspAdapter {
 7416            capabilities: lsp::ServerCapabilities {
 7417                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7418                ..Default::default()
 7419            },
 7420            ..Default::default()
 7421        },
 7422    );
 7423
 7424    let worktree = project.update(cx, |project, cx| {
 7425        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7426        assert_eq!(worktrees.len(), 1);
 7427        worktrees.pop().unwrap()
 7428    });
 7429    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7430
 7431    let buffer_1 = project
 7432        .update(cx, |project, cx| {
 7433            project.open_buffer((worktree_id, "main.rs"), cx)
 7434        })
 7435        .await
 7436        .unwrap();
 7437    let buffer_2 = project
 7438        .update(cx, |project, cx| {
 7439            project.open_buffer((worktree_id, "other.rs"), cx)
 7440        })
 7441        .await
 7442        .unwrap();
 7443    let buffer_3 = project
 7444        .update(cx, |project, cx| {
 7445            project.open_buffer((worktree_id, "lib.rs"), cx)
 7446        })
 7447        .await
 7448        .unwrap();
 7449
 7450    let multi_buffer = cx.new(|cx| {
 7451        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7452        multi_buffer.push_excerpts(
 7453            buffer_1.clone(),
 7454            [
 7455                ExcerptRange {
 7456                    context: Point::new(0, 0)..Point::new(3, 0),
 7457                    primary: None,
 7458                },
 7459                ExcerptRange {
 7460                    context: Point::new(5, 0)..Point::new(7, 0),
 7461                    primary: None,
 7462                },
 7463                ExcerptRange {
 7464                    context: Point::new(9, 0)..Point::new(10, 4),
 7465                    primary: None,
 7466                },
 7467            ],
 7468            cx,
 7469        );
 7470        multi_buffer.push_excerpts(
 7471            buffer_2.clone(),
 7472            [
 7473                ExcerptRange {
 7474                    context: Point::new(0, 0)..Point::new(3, 0),
 7475                    primary: None,
 7476                },
 7477                ExcerptRange {
 7478                    context: Point::new(5, 0)..Point::new(7, 0),
 7479                    primary: None,
 7480                },
 7481                ExcerptRange {
 7482                    context: Point::new(9, 0)..Point::new(10, 4),
 7483                    primary: None,
 7484                },
 7485            ],
 7486            cx,
 7487        );
 7488        multi_buffer.push_excerpts(
 7489            buffer_3.clone(),
 7490            [
 7491                ExcerptRange {
 7492                    context: Point::new(0, 0)..Point::new(3, 0),
 7493                    primary: None,
 7494                },
 7495                ExcerptRange {
 7496                    context: Point::new(5, 0)..Point::new(7, 0),
 7497                    primary: None,
 7498                },
 7499                ExcerptRange {
 7500                    context: Point::new(9, 0)..Point::new(10, 4),
 7501                    primary: None,
 7502                },
 7503            ],
 7504            cx,
 7505        );
 7506        multi_buffer
 7507    });
 7508    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7509        Editor::new(
 7510            EditorMode::Full,
 7511            multi_buffer,
 7512            Some(project.clone()),
 7513            true,
 7514            window,
 7515            cx,
 7516        )
 7517    });
 7518
 7519    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7520        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7521            s.select_ranges(Some(1..2))
 7522        });
 7523        editor.insert("|one|two|three|", window, cx);
 7524    });
 7525    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7526    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7527        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7528            s.select_ranges(Some(60..70))
 7529        });
 7530        editor.insert("|four|five|six|", window, cx);
 7531    });
 7532    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7533
 7534    // First two buffers should be edited, but not the third one.
 7535    assert_eq!(
 7536        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7537        "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}",
 7538    );
 7539    buffer_1.update(cx, |buffer, _| {
 7540        assert!(buffer.is_dirty());
 7541        assert_eq!(
 7542            buffer.text(),
 7543            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7544        )
 7545    });
 7546    buffer_2.update(cx, |buffer, _| {
 7547        assert!(buffer.is_dirty());
 7548        assert_eq!(
 7549            buffer.text(),
 7550            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7551        )
 7552    });
 7553    buffer_3.update(cx, |buffer, _| {
 7554        assert!(!buffer.is_dirty());
 7555        assert_eq!(buffer.text(), sample_text_3,)
 7556    });
 7557    cx.executor().run_until_parked();
 7558
 7559    cx.executor().start_waiting();
 7560    let save = multi_buffer_editor
 7561        .update_in(cx, |editor, window, cx| {
 7562            editor.save(true, project.clone(), window, cx)
 7563        })
 7564        .unwrap();
 7565
 7566    let fake_server = fake_servers.next().await.unwrap();
 7567    fake_server
 7568        .server
 7569        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7570            Ok(Some(vec![lsp::TextEdit::new(
 7571                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7572                format!("[{} formatted]", params.text_document.uri),
 7573            )]))
 7574        })
 7575        .detach();
 7576    save.await;
 7577
 7578    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7579    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7580    assert_eq!(
 7581        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7582        uri!("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}"),
 7583    );
 7584    buffer_1.update(cx, |buffer, _| {
 7585        assert!(!buffer.is_dirty());
 7586        assert_eq!(
 7587            buffer.text(),
 7588            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7589        )
 7590    });
 7591    buffer_2.update(cx, |buffer, _| {
 7592        assert!(!buffer.is_dirty());
 7593        assert_eq!(
 7594            buffer.text(),
 7595            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7596        )
 7597    });
 7598    buffer_3.update(cx, |buffer, _| {
 7599        assert!(!buffer.is_dirty());
 7600        assert_eq!(buffer.text(), sample_text_3,)
 7601    });
 7602}
 7603
 7604#[gpui::test]
 7605async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7606    init_test(cx, |_| {});
 7607
 7608    let fs = FakeFs::new(cx.executor());
 7609    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7610
 7611    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7612
 7613    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7614    language_registry.add(rust_lang());
 7615    let mut fake_servers = language_registry.register_fake_lsp(
 7616        "Rust",
 7617        FakeLspAdapter {
 7618            capabilities: lsp::ServerCapabilities {
 7619                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7620                ..Default::default()
 7621            },
 7622            ..Default::default()
 7623        },
 7624    );
 7625
 7626    let buffer = project
 7627        .update(cx, |project, cx| {
 7628            project.open_local_buffer(path!("/file.rs"), cx)
 7629        })
 7630        .await
 7631        .unwrap();
 7632
 7633    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7634    let (editor, cx) = cx.add_window_view(|window, cx| {
 7635        build_editor_with_project(project.clone(), buffer, window, cx)
 7636    });
 7637    editor.update_in(cx, |editor, window, cx| {
 7638        editor.set_text("one\ntwo\nthree\n", window, cx)
 7639    });
 7640    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7641
 7642    cx.executor().start_waiting();
 7643    let fake_server = fake_servers.next().await.unwrap();
 7644
 7645    let save = editor
 7646        .update_in(cx, |editor, window, cx| {
 7647            editor.save(true, project.clone(), window, cx)
 7648        })
 7649        .unwrap();
 7650    fake_server
 7651        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7652            assert_eq!(
 7653                params.text_document.uri,
 7654                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7655            );
 7656            assert_eq!(params.options.tab_size, 4);
 7657            Ok(Some(vec![lsp::TextEdit::new(
 7658                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7659                ", ".to_string(),
 7660            )]))
 7661        })
 7662        .next()
 7663        .await;
 7664    cx.executor().start_waiting();
 7665    save.await;
 7666    assert_eq!(
 7667        editor.update(cx, |editor, cx| editor.text(cx)),
 7668        "one, two\nthree\n"
 7669    );
 7670    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7671
 7672    editor.update_in(cx, |editor, window, cx| {
 7673        editor.set_text("one\ntwo\nthree\n", window, cx)
 7674    });
 7675    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7676
 7677    // Ensure we can still save even if formatting hangs.
 7678    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7679        move |params, _| async move {
 7680            assert_eq!(
 7681                params.text_document.uri,
 7682                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7683            );
 7684            futures::future::pending::<()>().await;
 7685            unreachable!()
 7686        },
 7687    );
 7688    let save = editor
 7689        .update_in(cx, |editor, window, cx| {
 7690            editor.save(true, project.clone(), window, cx)
 7691        })
 7692        .unwrap();
 7693    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7694    cx.executor().start_waiting();
 7695    save.await;
 7696    assert_eq!(
 7697        editor.update(cx, |editor, cx| editor.text(cx)),
 7698        "one\ntwo\nthree\n"
 7699    );
 7700    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7701
 7702    // For non-dirty buffer, no formatting request should be sent
 7703    let save = editor
 7704        .update_in(cx, |editor, window, cx| {
 7705            editor.save(true, project.clone(), window, cx)
 7706        })
 7707        .unwrap();
 7708    let _pending_format_request = fake_server
 7709        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7710            panic!("Should not be invoked on non-dirty buffer");
 7711        })
 7712        .next();
 7713    cx.executor().start_waiting();
 7714    save.await;
 7715
 7716    // Set Rust language override and assert overridden tabsize is sent to language server
 7717    update_test_language_settings(cx, |settings| {
 7718        settings.languages.insert(
 7719            "Rust".into(),
 7720            LanguageSettingsContent {
 7721                tab_size: NonZeroU32::new(8),
 7722                ..Default::default()
 7723            },
 7724        );
 7725    });
 7726
 7727    editor.update_in(cx, |editor, window, cx| {
 7728        editor.set_text("somehting_new\n", window, cx)
 7729    });
 7730    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7731    let save = editor
 7732        .update_in(cx, |editor, window, cx| {
 7733            editor.save(true, project.clone(), window, cx)
 7734        })
 7735        .unwrap();
 7736    fake_server
 7737        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7738            assert_eq!(
 7739                params.text_document.uri,
 7740                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7741            );
 7742            assert_eq!(params.options.tab_size, 8);
 7743            Ok(Some(vec![]))
 7744        })
 7745        .next()
 7746        .await;
 7747    cx.executor().start_waiting();
 7748    save.await;
 7749}
 7750
 7751#[gpui::test]
 7752async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7753    init_test(cx, |settings| {
 7754        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7755            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7756        ))
 7757    });
 7758
 7759    let fs = FakeFs::new(cx.executor());
 7760    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7761
 7762    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7763
 7764    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7765    language_registry.add(Arc::new(Language::new(
 7766        LanguageConfig {
 7767            name: "Rust".into(),
 7768            matcher: LanguageMatcher {
 7769                path_suffixes: vec!["rs".to_string()],
 7770                ..Default::default()
 7771            },
 7772            ..LanguageConfig::default()
 7773        },
 7774        Some(tree_sitter_rust::LANGUAGE.into()),
 7775    )));
 7776    update_test_language_settings(cx, |settings| {
 7777        // Enable Prettier formatting for the same buffer, and ensure
 7778        // LSP is called instead of Prettier.
 7779        settings.defaults.prettier = Some(PrettierSettings {
 7780            allowed: true,
 7781            ..PrettierSettings::default()
 7782        });
 7783    });
 7784    let mut fake_servers = language_registry.register_fake_lsp(
 7785        "Rust",
 7786        FakeLspAdapter {
 7787            capabilities: lsp::ServerCapabilities {
 7788                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7789                ..Default::default()
 7790            },
 7791            ..Default::default()
 7792        },
 7793    );
 7794
 7795    let buffer = project
 7796        .update(cx, |project, cx| {
 7797            project.open_local_buffer(path!("/file.rs"), cx)
 7798        })
 7799        .await
 7800        .unwrap();
 7801
 7802    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7803    let (editor, cx) = cx.add_window_view(|window, cx| {
 7804        build_editor_with_project(project.clone(), buffer, window, cx)
 7805    });
 7806    editor.update_in(cx, |editor, window, cx| {
 7807        editor.set_text("one\ntwo\nthree\n", window, cx)
 7808    });
 7809
 7810    cx.executor().start_waiting();
 7811    let fake_server = fake_servers.next().await.unwrap();
 7812
 7813    let format = editor
 7814        .update_in(cx, |editor, window, cx| {
 7815            editor.perform_format(
 7816                project.clone(),
 7817                FormatTrigger::Manual,
 7818                FormatTarget::Buffers,
 7819                window,
 7820                cx,
 7821            )
 7822        })
 7823        .unwrap();
 7824    fake_server
 7825        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7826            assert_eq!(
 7827                params.text_document.uri,
 7828                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7829            );
 7830            assert_eq!(params.options.tab_size, 4);
 7831            Ok(Some(vec![lsp::TextEdit::new(
 7832                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7833                ", ".to_string(),
 7834            )]))
 7835        })
 7836        .next()
 7837        .await;
 7838    cx.executor().start_waiting();
 7839    format.await;
 7840    assert_eq!(
 7841        editor.update(cx, |editor, cx| editor.text(cx)),
 7842        "one, two\nthree\n"
 7843    );
 7844
 7845    editor.update_in(cx, |editor, window, cx| {
 7846        editor.set_text("one\ntwo\nthree\n", window, cx)
 7847    });
 7848    // Ensure we don't lock if formatting hangs.
 7849    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7850        assert_eq!(
 7851            params.text_document.uri,
 7852            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7853        );
 7854        futures::future::pending::<()>().await;
 7855        unreachable!()
 7856    });
 7857    let format = editor
 7858        .update_in(cx, |editor, window, cx| {
 7859            editor.perform_format(
 7860                project,
 7861                FormatTrigger::Manual,
 7862                FormatTarget::Buffers,
 7863                window,
 7864                cx,
 7865            )
 7866        })
 7867        .unwrap();
 7868    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7869    cx.executor().start_waiting();
 7870    format.await;
 7871    assert_eq!(
 7872        editor.update(cx, |editor, cx| editor.text(cx)),
 7873        "one\ntwo\nthree\n"
 7874    );
 7875}
 7876
 7877#[gpui::test]
 7878async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 7879    init_test(cx, |_| {});
 7880
 7881    let mut cx = EditorLspTestContext::new_rust(
 7882        lsp::ServerCapabilities {
 7883            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7884            ..Default::default()
 7885        },
 7886        cx,
 7887    )
 7888    .await;
 7889
 7890    cx.set_state(indoc! {"
 7891        one.twoˇ
 7892    "});
 7893
 7894    // The format request takes a long time. When it completes, it inserts
 7895    // a newline and an indent before the `.`
 7896    cx.lsp
 7897        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7898            let executor = cx.background_executor().clone();
 7899            async move {
 7900                executor.timer(Duration::from_millis(100)).await;
 7901                Ok(Some(vec![lsp::TextEdit {
 7902                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7903                    new_text: "\n    ".into(),
 7904                }]))
 7905            }
 7906        });
 7907
 7908    // Submit a format request.
 7909    let format_1 = cx
 7910        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7911        .unwrap();
 7912    cx.executor().run_until_parked();
 7913
 7914    // Submit a second format request.
 7915    let format_2 = cx
 7916        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7917        .unwrap();
 7918    cx.executor().run_until_parked();
 7919
 7920    // Wait for both format requests to complete
 7921    cx.executor().advance_clock(Duration::from_millis(200));
 7922    cx.executor().start_waiting();
 7923    format_1.await.unwrap();
 7924    cx.executor().start_waiting();
 7925    format_2.await.unwrap();
 7926
 7927    // The formatting edits only happens once.
 7928    cx.assert_editor_state(indoc! {"
 7929        one
 7930            .twoˇ
 7931    "});
 7932}
 7933
 7934#[gpui::test]
 7935async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 7936    init_test(cx, |settings| {
 7937        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7938    });
 7939
 7940    let mut cx = EditorLspTestContext::new_rust(
 7941        lsp::ServerCapabilities {
 7942            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7943            ..Default::default()
 7944        },
 7945        cx,
 7946    )
 7947    .await;
 7948
 7949    // Set up a buffer white some trailing whitespace and no trailing newline.
 7950    cx.set_state(
 7951        &[
 7952            "one ",   //
 7953            "twoˇ",   //
 7954            "three ", //
 7955            "four",   //
 7956        ]
 7957        .join("\n"),
 7958    );
 7959
 7960    // Submit a format request.
 7961    let format = cx
 7962        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7963        .unwrap();
 7964
 7965    // Record which buffer changes have been sent to the language server
 7966    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7967    cx.lsp
 7968        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7969            let buffer_changes = buffer_changes.clone();
 7970            move |params, _| {
 7971                buffer_changes.lock().extend(
 7972                    params
 7973                        .content_changes
 7974                        .into_iter()
 7975                        .map(|e| (e.range.unwrap(), e.text)),
 7976                );
 7977            }
 7978        });
 7979
 7980    // Handle formatting requests to the language server.
 7981    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7982        let buffer_changes = buffer_changes.clone();
 7983        move |_, _| {
 7984            // When formatting is requested, trailing whitespace has already been stripped,
 7985            // and the trailing newline has already been added.
 7986            assert_eq!(
 7987                &buffer_changes.lock()[1..],
 7988                &[
 7989                    (
 7990                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7991                        "".into()
 7992                    ),
 7993                    (
 7994                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7995                        "".into()
 7996                    ),
 7997                    (
 7998                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7999                        "\n".into()
 8000                    ),
 8001                ]
 8002            );
 8003
 8004            // Insert blank lines between each line of the buffer.
 8005            async move {
 8006                Ok(Some(vec![
 8007                    lsp::TextEdit {
 8008                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8009                        new_text: "\n".into(),
 8010                    },
 8011                    lsp::TextEdit {
 8012                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8013                        new_text: "\n".into(),
 8014                    },
 8015                ]))
 8016            }
 8017        }
 8018    });
 8019
 8020    // After formatting the buffer, the trailing whitespace is stripped,
 8021    // a newline is appended, and the edits provided by the language server
 8022    // have been applied.
 8023    format.await.unwrap();
 8024    cx.assert_editor_state(
 8025        &[
 8026            "one",   //
 8027            "",      //
 8028            "twoˇ",  //
 8029            "",      //
 8030            "three", //
 8031            "four",  //
 8032            "",      //
 8033        ]
 8034        .join("\n"),
 8035    );
 8036
 8037    // Undoing the formatting undoes the trailing whitespace removal, the
 8038    // trailing newline, and the LSP edits.
 8039    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8040    cx.assert_editor_state(
 8041        &[
 8042            "one ",   //
 8043            "twoˇ",   //
 8044            "three ", //
 8045            "four",   //
 8046        ]
 8047        .join("\n"),
 8048    );
 8049}
 8050
 8051#[gpui::test]
 8052async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8053    cx: &mut TestAppContext,
 8054) {
 8055    init_test(cx, |_| {});
 8056
 8057    cx.update(|cx| {
 8058        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8059            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8060                settings.auto_signature_help = Some(true);
 8061            });
 8062        });
 8063    });
 8064
 8065    let mut cx = EditorLspTestContext::new_rust(
 8066        lsp::ServerCapabilities {
 8067            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8068                ..Default::default()
 8069            }),
 8070            ..Default::default()
 8071        },
 8072        cx,
 8073    )
 8074    .await;
 8075
 8076    let language = Language::new(
 8077        LanguageConfig {
 8078            name: "Rust".into(),
 8079            brackets: BracketPairConfig {
 8080                pairs: vec![
 8081                    BracketPair {
 8082                        start: "{".to_string(),
 8083                        end: "}".to_string(),
 8084                        close: true,
 8085                        surround: true,
 8086                        newline: true,
 8087                    },
 8088                    BracketPair {
 8089                        start: "(".to_string(),
 8090                        end: ")".to_string(),
 8091                        close: true,
 8092                        surround: true,
 8093                        newline: true,
 8094                    },
 8095                    BracketPair {
 8096                        start: "/*".to_string(),
 8097                        end: " */".to_string(),
 8098                        close: true,
 8099                        surround: true,
 8100                        newline: true,
 8101                    },
 8102                    BracketPair {
 8103                        start: "[".to_string(),
 8104                        end: "]".to_string(),
 8105                        close: false,
 8106                        surround: false,
 8107                        newline: true,
 8108                    },
 8109                    BracketPair {
 8110                        start: "\"".to_string(),
 8111                        end: "\"".to_string(),
 8112                        close: true,
 8113                        surround: true,
 8114                        newline: false,
 8115                    },
 8116                    BracketPair {
 8117                        start: "<".to_string(),
 8118                        end: ">".to_string(),
 8119                        close: false,
 8120                        surround: true,
 8121                        newline: true,
 8122                    },
 8123                ],
 8124                ..Default::default()
 8125            },
 8126            autoclose_before: "})]".to_string(),
 8127            ..Default::default()
 8128        },
 8129        Some(tree_sitter_rust::LANGUAGE.into()),
 8130    );
 8131    let language = Arc::new(language);
 8132
 8133    cx.language_registry().add(language.clone());
 8134    cx.update_buffer(|buffer, cx| {
 8135        buffer.set_language(Some(language), cx);
 8136    });
 8137
 8138    cx.set_state(
 8139        &r#"
 8140            fn main() {
 8141                sampleˇ
 8142            }
 8143        "#
 8144        .unindent(),
 8145    );
 8146
 8147    cx.update_editor(|editor, window, cx| {
 8148        editor.handle_input("(", window, cx);
 8149    });
 8150    cx.assert_editor_state(
 8151        &"
 8152            fn main() {
 8153                sample(ˇ)
 8154            }
 8155        "
 8156        .unindent(),
 8157    );
 8158
 8159    let mocked_response = lsp::SignatureHelp {
 8160        signatures: vec![lsp::SignatureInformation {
 8161            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8162            documentation: None,
 8163            parameters: Some(vec![
 8164                lsp::ParameterInformation {
 8165                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8166                    documentation: None,
 8167                },
 8168                lsp::ParameterInformation {
 8169                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8170                    documentation: None,
 8171                },
 8172            ]),
 8173            active_parameter: None,
 8174        }],
 8175        active_signature: Some(0),
 8176        active_parameter: Some(0),
 8177    };
 8178    handle_signature_help_request(&mut cx, mocked_response).await;
 8179
 8180    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8181        .await;
 8182
 8183    cx.editor(|editor, _, _| {
 8184        let signature_help_state = editor.signature_help_state.popover().cloned();
 8185        assert_eq!(
 8186            signature_help_state.unwrap().label,
 8187            "param1: u8, param2: u8"
 8188        );
 8189    });
 8190}
 8191
 8192#[gpui::test]
 8193async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8194    init_test(cx, |_| {});
 8195
 8196    cx.update(|cx| {
 8197        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8198            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8199                settings.auto_signature_help = Some(false);
 8200                settings.show_signature_help_after_edits = Some(false);
 8201            });
 8202        });
 8203    });
 8204
 8205    let mut cx = EditorLspTestContext::new_rust(
 8206        lsp::ServerCapabilities {
 8207            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8208                ..Default::default()
 8209            }),
 8210            ..Default::default()
 8211        },
 8212        cx,
 8213    )
 8214    .await;
 8215
 8216    let language = Language::new(
 8217        LanguageConfig {
 8218            name: "Rust".into(),
 8219            brackets: BracketPairConfig {
 8220                pairs: vec![
 8221                    BracketPair {
 8222                        start: "{".to_string(),
 8223                        end: "}".to_string(),
 8224                        close: true,
 8225                        surround: true,
 8226                        newline: true,
 8227                    },
 8228                    BracketPair {
 8229                        start: "(".to_string(),
 8230                        end: ")".to_string(),
 8231                        close: true,
 8232                        surround: true,
 8233                        newline: true,
 8234                    },
 8235                    BracketPair {
 8236                        start: "/*".to_string(),
 8237                        end: " */".to_string(),
 8238                        close: true,
 8239                        surround: true,
 8240                        newline: true,
 8241                    },
 8242                    BracketPair {
 8243                        start: "[".to_string(),
 8244                        end: "]".to_string(),
 8245                        close: false,
 8246                        surround: false,
 8247                        newline: true,
 8248                    },
 8249                    BracketPair {
 8250                        start: "\"".to_string(),
 8251                        end: "\"".to_string(),
 8252                        close: true,
 8253                        surround: true,
 8254                        newline: false,
 8255                    },
 8256                    BracketPair {
 8257                        start: "<".to_string(),
 8258                        end: ">".to_string(),
 8259                        close: false,
 8260                        surround: true,
 8261                        newline: true,
 8262                    },
 8263                ],
 8264                ..Default::default()
 8265            },
 8266            autoclose_before: "})]".to_string(),
 8267            ..Default::default()
 8268        },
 8269        Some(tree_sitter_rust::LANGUAGE.into()),
 8270    );
 8271    let language = Arc::new(language);
 8272
 8273    cx.language_registry().add(language.clone());
 8274    cx.update_buffer(|buffer, cx| {
 8275        buffer.set_language(Some(language), cx);
 8276    });
 8277
 8278    // Ensure that signature_help is not called when no signature help is enabled.
 8279    cx.set_state(
 8280        &r#"
 8281            fn main() {
 8282                sampleˇ
 8283            }
 8284        "#
 8285        .unindent(),
 8286    );
 8287    cx.update_editor(|editor, window, cx| {
 8288        editor.handle_input("(", window, cx);
 8289    });
 8290    cx.assert_editor_state(
 8291        &"
 8292            fn main() {
 8293                sample(ˇ)
 8294            }
 8295        "
 8296        .unindent(),
 8297    );
 8298    cx.editor(|editor, _, _| {
 8299        assert!(editor.signature_help_state.task().is_none());
 8300    });
 8301
 8302    let mocked_response = lsp::SignatureHelp {
 8303        signatures: vec![lsp::SignatureInformation {
 8304            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8305            documentation: None,
 8306            parameters: Some(vec![
 8307                lsp::ParameterInformation {
 8308                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8309                    documentation: None,
 8310                },
 8311                lsp::ParameterInformation {
 8312                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8313                    documentation: None,
 8314                },
 8315            ]),
 8316            active_parameter: None,
 8317        }],
 8318        active_signature: Some(0),
 8319        active_parameter: Some(0),
 8320    };
 8321
 8322    // Ensure that signature_help is called when enabled afte edits
 8323    cx.update(|_, cx| {
 8324        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8325            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8326                settings.auto_signature_help = Some(false);
 8327                settings.show_signature_help_after_edits = Some(true);
 8328            });
 8329        });
 8330    });
 8331    cx.set_state(
 8332        &r#"
 8333            fn main() {
 8334                sampleˇ
 8335            }
 8336        "#
 8337        .unindent(),
 8338    );
 8339    cx.update_editor(|editor, window, cx| {
 8340        editor.handle_input("(", window, cx);
 8341    });
 8342    cx.assert_editor_state(
 8343        &"
 8344            fn main() {
 8345                sample(ˇ)
 8346            }
 8347        "
 8348        .unindent(),
 8349    );
 8350    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8351    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8352        .await;
 8353    cx.update_editor(|editor, _, _| {
 8354        let signature_help_state = editor.signature_help_state.popover().cloned();
 8355        assert!(signature_help_state.is_some());
 8356        assert_eq!(
 8357            signature_help_state.unwrap().label,
 8358            "param1: u8, param2: u8"
 8359        );
 8360        editor.signature_help_state = SignatureHelpState::default();
 8361    });
 8362
 8363    // Ensure that signature_help is called when auto signature help override is enabled
 8364    cx.update(|_, cx| {
 8365        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8366            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8367                settings.auto_signature_help = Some(true);
 8368                settings.show_signature_help_after_edits = Some(false);
 8369            });
 8370        });
 8371    });
 8372    cx.set_state(
 8373        &r#"
 8374            fn main() {
 8375                sampleˇ
 8376            }
 8377        "#
 8378        .unindent(),
 8379    );
 8380    cx.update_editor(|editor, window, cx| {
 8381        editor.handle_input("(", window, cx);
 8382    });
 8383    cx.assert_editor_state(
 8384        &"
 8385            fn main() {
 8386                sample(ˇ)
 8387            }
 8388        "
 8389        .unindent(),
 8390    );
 8391    handle_signature_help_request(&mut cx, mocked_response).await;
 8392    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8393        .await;
 8394    cx.editor(|editor, _, _| {
 8395        let signature_help_state = editor.signature_help_state.popover().cloned();
 8396        assert!(signature_help_state.is_some());
 8397        assert_eq!(
 8398            signature_help_state.unwrap().label,
 8399            "param1: u8, param2: u8"
 8400        );
 8401    });
 8402}
 8403
 8404#[gpui::test]
 8405async fn test_signature_help(cx: &mut TestAppContext) {
 8406    init_test(cx, |_| {});
 8407    cx.update(|cx| {
 8408        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8409            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8410                settings.auto_signature_help = Some(true);
 8411            });
 8412        });
 8413    });
 8414
 8415    let mut cx = EditorLspTestContext::new_rust(
 8416        lsp::ServerCapabilities {
 8417            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8418                ..Default::default()
 8419            }),
 8420            ..Default::default()
 8421        },
 8422        cx,
 8423    )
 8424    .await;
 8425
 8426    // A test that directly calls `show_signature_help`
 8427    cx.update_editor(|editor, window, cx| {
 8428        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8429    });
 8430
 8431    let mocked_response = lsp::SignatureHelp {
 8432        signatures: vec![lsp::SignatureInformation {
 8433            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8434            documentation: None,
 8435            parameters: Some(vec![
 8436                lsp::ParameterInformation {
 8437                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8438                    documentation: None,
 8439                },
 8440                lsp::ParameterInformation {
 8441                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8442                    documentation: None,
 8443                },
 8444            ]),
 8445            active_parameter: None,
 8446        }],
 8447        active_signature: Some(0),
 8448        active_parameter: Some(0),
 8449    };
 8450    handle_signature_help_request(&mut cx, mocked_response).await;
 8451
 8452    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8453        .await;
 8454
 8455    cx.editor(|editor, _, _| {
 8456        let signature_help_state = editor.signature_help_state.popover().cloned();
 8457        assert!(signature_help_state.is_some());
 8458        assert_eq!(
 8459            signature_help_state.unwrap().label,
 8460            "param1: u8, param2: u8"
 8461        );
 8462    });
 8463
 8464    // When exiting outside from inside the brackets, `signature_help` is closed.
 8465    cx.set_state(indoc! {"
 8466        fn main() {
 8467            sample(ˇ);
 8468        }
 8469
 8470        fn sample(param1: u8, param2: u8) {}
 8471    "});
 8472
 8473    cx.update_editor(|editor, window, cx| {
 8474        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8475    });
 8476
 8477    let mocked_response = lsp::SignatureHelp {
 8478        signatures: Vec::new(),
 8479        active_signature: None,
 8480        active_parameter: None,
 8481    };
 8482    handle_signature_help_request(&mut cx, mocked_response).await;
 8483
 8484    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8485        .await;
 8486
 8487    cx.editor(|editor, _, _| {
 8488        assert!(!editor.signature_help_state.is_shown());
 8489    });
 8490
 8491    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8492    cx.set_state(indoc! {"
 8493        fn main() {
 8494            sample(ˇ);
 8495        }
 8496
 8497        fn sample(param1: u8, param2: u8) {}
 8498    "});
 8499
 8500    let mocked_response = lsp::SignatureHelp {
 8501        signatures: vec![lsp::SignatureInformation {
 8502            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8503            documentation: None,
 8504            parameters: Some(vec![
 8505                lsp::ParameterInformation {
 8506                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8507                    documentation: None,
 8508                },
 8509                lsp::ParameterInformation {
 8510                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8511                    documentation: None,
 8512                },
 8513            ]),
 8514            active_parameter: None,
 8515        }],
 8516        active_signature: Some(0),
 8517        active_parameter: Some(0),
 8518    };
 8519    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8520    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8521        .await;
 8522    cx.editor(|editor, _, _| {
 8523        assert!(editor.signature_help_state.is_shown());
 8524    });
 8525
 8526    // Restore the popover with more parameter input
 8527    cx.set_state(indoc! {"
 8528        fn main() {
 8529            sample(param1, param2ˇ);
 8530        }
 8531
 8532        fn sample(param1: u8, param2: u8) {}
 8533    "});
 8534
 8535    let mocked_response = lsp::SignatureHelp {
 8536        signatures: vec![lsp::SignatureInformation {
 8537            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8538            documentation: None,
 8539            parameters: Some(vec![
 8540                lsp::ParameterInformation {
 8541                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8542                    documentation: None,
 8543                },
 8544                lsp::ParameterInformation {
 8545                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8546                    documentation: None,
 8547                },
 8548            ]),
 8549            active_parameter: None,
 8550        }],
 8551        active_signature: Some(0),
 8552        active_parameter: Some(1),
 8553    };
 8554    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8555    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8556        .await;
 8557
 8558    // When selecting a range, the popover is gone.
 8559    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8560    cx.update_editor(|editor, window, cx| {
 8561        editor.change_selections(None, window, cx, |s| {
 8562            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8563        })
 8564    });
 8565    cx.assert_editor_state(indoc! {"
 8566        fn main() {
 8567            sample(param1, «ˇparam2»);
 8568        }
 8569
 8570        fn sample(param1: u8, param2: u8) {}
 8571    "});
 8572    cx.editor(|editor, _, _| {
 8573        assert!(!editor.signature_help_state.is_shown());
 8574    });
 8575
 8576    // When unselecting again, the popover is back if within the brackets.
 8577    cx.update_editor(|editor, window, cx| {
 8578        editor.change_selections(None, window, cx, |s| {
 8579            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8580        })
 8581    });
 8582    cx.assert_editor_state(indoc! {"
 8583        fn main() {
 8584            sample(param1, ˇparam2);
 8585        }
 8586
 8587        fn sample(param1: u8, param2: u8) {}
 8588    "});
 8589    handle_signature_help_request(&mut cx, mocked_response).await;
 8590    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8591        .await;
 8592    cx.editor(|editor, _, _| {
 8593        assert!(editor.signature_help_state.is_shown());
 8594    });
 8595
 8596    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8597    cx.update_editor(|editor, window, cx| {
 8598        editor.change_selections(None, window, cx, |s| {
 8599            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8600            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8601        })
 8602    });
 8603    cx.assert_editor_state(indoc! {"
 8604        fn main() {
 8605            sample(param1, ˇparam2);
 8606        }
 8607
 8608        fn sample(param1: u8, param2: u8) {}
 8609    "});
 8610
 8611    let mocked_response = lsp::SignatureHelp {
 8612        signatures: vec![lsp::SignatureInformation {
 8613            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8614            documentation: None,
 8615            parameters: Some(vec![
 8616                lsp::ParameterInformation {
 8617                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8618                    documentation: None,
 8619                },
 8620                lsp::ParameterInformation {
 8621                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8622                    documentation: None,
 8623                },
 8624            ]),
 8625            active_parameter: None,
 8626        }],
 8627        active_signature: Some(0),
 8628        active_parameter: Some(1),
 8629    };
 8630    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8631    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8632        .await;
 8633    cx.update_editor(|editor, _, cx| {
 8634        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8635    });
 8636    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8637        .await;
 8638    cx.update_editor(|editor, window, cx| {
 8639        editor.change_selections(None, window, cx, |s| {
 8640            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8641        })
 8642    });
 8643    cx.assert_editor_state(indoc! {"
 8644        fn main() {
 8645            sample(param1, «ˇparam2»);
 8646        }
 8647
 8648        fn sample(param1: u8, param2: u8) {}
 8649    "});
 8650    cx.update_editor(|editor, window, cx| {
 8651        editor.change_selections(None, window, cx, |s| {
 8652            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8653        })
 8654    });
 8655    cx.assert_editor_state(indoc! {"
 8656        fn main() {
 8657            sample(param1, ˇparam2);
 8658        }
 8659
 8660        fn sample(param1: u8, param2: u8) {}
 8661    "});
 8662    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8663        .await;
 8664}
 8665
 8666#[gpui::test]
 8667async fn test_completion(cx: &mut TestAppContext) {
 8668    init_test(cx, |_| {});
 8669
 8670    let mut cx = EditorLspTestContext::new_rust(
 8671        lsp::ServerCapabilities {
 8672            completion_provider: Some(lsp::CompletionOptions {
 8673                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8674                resolve_provider: Some(true),
 8675                ..Default::default()
 8676            }),
 8677            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8678            ..Default::default()
 8679        },
 8680        cx,
 8681    )
 8682    .await;
 8683    let counter = Arc::new(AtomicUsize::new(0));
 8684
 8685    cx.set_state(indoc! {"
 8686        oneˇ
 8687        two
 8688        three
 8689    "});
 8690    cx.simulate_keystroke(".");
 8691    handle_completion_request(
 8692        &mut cx,
 8693        indoc! {"
 8694            one.|<>
 8695            two
 8696            three
 8697        "},
 8698        vec!["first_completion", "second_completion"],
 8699        counter.clone(),
 8700    )
 8701    .await;
 8702    cx.condition(|editor, _| editor.context_menu_visible())
 8703        .await;
 8704    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8705
 8706    let _handler = handle_signature_help_request(
 8707        &mut cx,
 8708        lsp::SignatureHelp {
 8709            signatures: vec![lsp::SignatureInformation {
 8710                label: "test signature".to_string(),
 8711                documentation: None,
 8712                parameters: Some(vec![lsp::ParameterInformation {
 8713                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8714                    documentation: None,
 8715                }]),
 8716                active_parameter: None,
 8717            }],
 8718            active_signature: None,
 8719            active_parameter: None,
 8720        },
 8721    );
 8722    cx.update_editor(|editor, window, cx| {
 8723        assert!(
 8724            !editor.signature_help_state.is_shown(),
 8725            "No signature help was called for"
 8726        );
 8727        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8728    });
 8729    cx.run_until_parked();
 8730    cx.update_editor(|editor, _, _| {
 8731        assert!(
 8732            !editor.signature_help_state.is_shown(),
 8733            "No signature help should be shown when completions menu is open"
 8734        );
 8735    });
 8736
 8737    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8738        editor.context_menu_next(&Default::default(), window, cx);
 8739        editor
 8740            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8741            .unwrap()
 8742    });
 8743    cx.assert_editor_state(indoc! {"
 8744        one.second_completionˇ
 8745        two
 8746        three
 8747    "});
 8748
 8749    handle_resolve_completion_request(
 8750        &mut cx,
 8751        Some(vec![
 8752            (
 8753                //This overlaps with the primary completion edit which is
 8754                //misbehavior from the LSP spec, test that we filter it out
 8755                indoc! {"
 8756                    one.second_ˇcompletion
 8757                    two
 8758                    threeˇ
 8759                "},
 8760                "overlapping additional edit",
 8761            ),
 8762            (
 8763                indoc! {"
 8764                    one.second_completion
 8765                    two
 8766                    threeˇ
 8767                "},
 8768                "\nadditional edit",
 8769            ),
 8770        ]),
 8771    )
 8772    .await;
 8773    apply_additional_edits.await.unwrap();
 8774    cx.assert_editor_state(indoc! {"
 8775        one.second_completionˇ
 8776        two
 8777        three
 8778        additional edit
 8779    "});
 8780
 8781    cx.set_state(indoc! {"
 8782        one.second_completion
 8783        twoˇ
 8784        threeˇ
 8785        additional edit
 8786    "});
 8787    cx.simulate_keystroke(" ");
 8788    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8789    cx.simulate_keystroke("s");
 8790    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8791
 8792    cx.assert_editor_state(indoc! {"
 8793        one.second_completion
 8794        two sˇ
 8795        three sˇ
 8796        additional edit
 8797    "});
 8798    handle_completion_request(
 8799        &mut cx,
 8800        indoc! {"
 8801            one.second_completion
 8802            two s
 8803            three <s|>
 8804            additional edit
 8805        "},
 8806        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8807        counter.clone(),
 8808    )
 8809    .await;
 8810    cx.condition(|editor, _| editor.context_menu_visible())
 8811        .await;
 8812    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8813
 8814    cx.simulate_keystroke("i");
 8815
 8816    handle_completion_request(
 8817        &mut cx,
 8818        indoc! {"
 8819            one.second_completion
 8820            two si
 8821            three <si|>
 8822            additional edit
 8823        "},
 8824        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8825        counter.clone(),
 8826    )
 8827    .await;
 8828    cx.condition(|editor, _| editor.context_menu_visible())
 8829        .await;
 8830    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8831
 8832    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8833        editor
 8834            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8835            .unwrap()
 8836    });
 8837    cx.assert_editor_state(indoc! {"
 8838        one.second_completion
 8839        two sixth_completionˇ
 8840        three sixth_completionˇ
 8841        additional edit
 8842    "});
 8843
 8844    apply_additional_edits.await.unwrap();
 8845
 8846    update_test_language_settings(&mut cx, |settings| {
 8847        settings.defaults.show_completions_on_input = Some(false);
 8848    });
 8849    cx.set_state("editorˇ");
 8850    cx.simulate_keystroke(".");
 8851    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8852    cx.simulate_keystroke("c");
 8853    cx.simulate_keystroke("l");
 8854    cx.simulate_keystroke("o");
 8855    cx.assert_editor_state("editor.cloˇ");
 8856    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8857    cx.update_editor(|editor, window, cx| {
 8858        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8859    });
 8860    handle_completion_request(
 8861        &mut cx,
 8862        "editor.<clo|>",
 8863        vec!["close", "clobber"],
 8864        counter.clone(),
 8865    )
 8866    .await;
 8867    cx.condition(|editor, _| editor.context_menu_visible())
 8868        .await;
 8869    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8870
 8871    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8872        editor
 8873            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8874            .unwrap()
 8875    });
 8876    cx.assert_editor_state("editor.closeˇ");
 8877    handle_resolve_completion_request(&mut cx, None).await;
 8878    apply_additional_edits.await.unwrap();
 8879}
 8880
 8881#[gpui::test]
 8882async fn test_multiline_completion(cx: &mut TestAppContext) {
 8883    init_test(cx, |_| {});
 8884
 8885    let fs = FakeFs::new(cx.executor());
 8886    fs.insert_tree(
 8887        path!("/a"),
 8888        json!({
 8889            "main.ts": "a",
 8890        }),
 8891    )
 8892    .await;
 8893
 8894    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8895    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8896    let typescript_language = Arc::new(Language::new(
 8897        LanguageConfig {
 8898            name: "TypeScript".into(),
 8899            matcher: LanguageMatcher {
 8900                path_suffixes: vec!["ts".to_string()],
 8901                ..LanguageMatcher::default()
 8902            },
 8903            line_comments: vec!["// ".into()],
 8904            ..LanguageConfig::default()
 8905        },
 8906        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8907    ));
 8908    language_registry.add(typescript_language.clone());
 8909    let mut fake_servers = language_registry.register_fake_lsp(
 8910        "TypeScript",
 8911        FakeLspAdapter {
 8912            capabilities: lsp::ServerCapabilities {
 8913                completion_provider: Some(lsp::CompletionOptions {
 8914                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8915                    ..lsp::CompletionOptions::default()
 8916                }),
 8917                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8918                ..lsp::ServerCapabilities::default()
 8919            },
 8920            // Emulate vtsls label generation
 8921            label_for_completion: Some(Box::new(|item, _| {
 8922                let text = if let Some(description) = item
 8923                    .label_details
 8924                    .as_ref()
 8925                    .and_then(|label_details| label_details.description.as_ref())
 8926                {
 8927                    format!("{} {}", item.label, description)
 8928                } else if let Some(detail) = &item.detail {
 8929                    format!("{} {}", item.label, detail)
 8930                } else {
 8931                    item.label.clone()
 8932                };
 8933                let len = text.len();
 8934                Some(language::CodeLabel {
 8935                    text,
 8936                    runs: Vec::new(),
 8937                    filter_range: 0..len,
 8938                })
 8939            })),
 8940            ..FakeLspAdapter::default()
 8941        },
 8942    );
 8943    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8944    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8945    let worktree_id = workspace
 8946        .update(cx, |workspace, _window, cx| {
 8947            workspace.project().update(cx, |project, cx| {
 8948                project.worktrees(cx).next().unwrap().read(cx).id()
 8949            })
 8950        })
 8951        .unwrap();
 8952    let _buffer = project
 8953        .update(cx, |project, cx| {
 8954            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8955        })
 8956        .await
 8957        .unwrap();
 8958    let editor = workspace
 8959        .update(cx, |workspace, window, cx| {
 8960            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8961        })
 8962        .unwrap()
 8963        .await
 8964        .unwrap()
 8965        .downcast::<Editor>()
 8966        .unwrap();
 8967    let fake_server = fake_servers.next().await.unwrap();
 8968
 8969    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8970    let multiline_label_2 = "a\nb\nc\n";
 8971    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8972    let multiline_description = "d\ne\nf\n";
 8973    let multiline_detail_2 = "g\nh\ni\n";
 8974
 8975    let mut completion_handle =
 8976        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8977            Ok(Some(lsp::CompletionResponse::Array(vec![
 8978                lsp::CompletionItem {
 8979                    label: multiline_label.to_string(),
 8980                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8981                        range: lsp::Range {
 8982                            start: lsp::Position {
 8983                                line: params.text_document_position.position.line,
 8984                                character: params.text_document_position.position.character,
 8985                            },
 8986                            end: lsp::Position {
 8987                                line: params.text_document_position.position.line,
 8988                                character: params.text_document_position.position.character,
 8989                            },
 8990                        },
 8991                        new_text: "new_text_1".to_string(),
 8992                    })),
 8993                    ..lsp::CompletionItem::default()
 8994                },
 8995                lsp::CompletionItem {
 8996                    label: "single line label 1".to_string(),
 8997                    detail: Some(multiline_detail.to_string()),
 8998                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8999                        range: lsp::Range {
 9000                            start: lsp::Position {
 9001                                line: params.text_document_position.position.line,
 9002                                character: params.text_document_position.position.character,
 9003                            },
 9004                            end: lsp::Position {
 9005                                line: params.text_document_position.position.line,
 9006                                character: params.text_document_position.position.character,
 9007                            },
 9008                        },
 9009                        new_text: "new_text_2".to_string(),
 9010                    })),
 9011                    ..lsp::CompletionItem::default()
 9012                },
 9013                lsp::CompletionItem {
 9014                    label: "single line label 2".to_string(),
 9015                    label_details: Some(lsp::CompletionItemLabelDetails {
 9016                        description: Some(multiline_description.to_string()),
 9017                        detail: None,
 9018                    }),
 9019                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9020                        range: lsp::Range {
 9021                            start: lsp::Position {
 9022                                line: params.text_document_position.position.line,
 9023                                character: params.text_document_position.position.character,
 9024                            },
 9025                            end: lsp::Position {
 9026                                line: params.text_document_position.position.line,
 9027                                character: params.text_document_position.position.character,
 9028                            },
 9029                        },
 9030                        new_text: "new_text_2".to_string(),
 9031                    })),
 9032                    ..lsp::CompletionItem::default()
 9033                },
 9034                lsp::CompletionItem {
 9035                    label: multiline_label_2.to_string(),
 9036                    detail: Some(multiline_detail_2.to_string()),
 9037                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9038                        range: lsp::Range {
 9039                            start: lsp::Position {
 9040                                line: params.text_document_position.position.line,
 9041                                character: params.text_document_position.position.character,
 9042                            },
 9043                            end: lsp::Position {
 9044                                line: params.text_document_position.position.line,
 9045                                character: params.text_document_position.position.character,
 9046                            },
 9047                        },
 9048                        new_text: "new_text_3".to_string(),
 9049                    })),
 9050                    ..lsp::CompletionItem::default()
 9051                },
 9052                lsp::CompletionItem {
 9053                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9054                    detail: Some(
 9055                        "Details with many     spaces and \t but without newlines".to_string(),
 9056                    ),
 9057                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9058                        range: lsp::Range {
 9059                            start: lsp::Position {
 9060                                line: params.text_document_position.position.line,
 9061                                character: params.text_document_position.position.character,
 9062                            },
 9063                            end: lsp::Position {
 9064                                line: params.text_document_position.position.line,
 9065                                character: params.text_document_position.position.character,
 9066                            },
 9067                        },
 9068                        new_text: "new_text_4".to_string(),
 9069                    })),
 9070                    ..lsp::CompletionItem::default()
 9071                },
 9072            ])))
 9073        });
 9074
 9075    editor.update_in(cx, |editor, window, cx| {
 9076        cx.focus_self(window);
 9077        editor.move_to_end(&MoveToEnd, window, cx);
 9078        editor.handle_input(".", window, cx);
 9079    });
 9080    cx.run_until_parked();
 9081    completion_handle.next().await.unwrap();
 9082
 9083    editor.update(cx, |editor, _| {
 9084        assert!(editor.context_menu_visible());
 9085        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9086        {
 9087            let completion_labels = menu
 9088                .completions
 9089                .borrow()
 9090                .iter()
 9091                .map(|c| c.label.text.clone())
 9092                .collect::<Vec<_>>();
 9093            assert_eq!(
 9094                completion_labels,
 9095                &[
 9096                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9097                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9098                    "single line label 2 d e f ",
 9099                    "a b c g h i ",
 9100                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9101                ],
 9102                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9103            );
 9104
 9105            for completion in menu
 9106                .completions
 9107                .borrow()
 9108                .iter() {
 9109                    assert_eq!(
 9110                        completion.label.filter_range,
 9111                        0..completion.label.text.len(),
 9112                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9113                    );
 9114                }
 9115
 9116        } else {
 9117            panic!("expected completion menu to be open");
 9118        }
 9119    });
 9120}
 9121
 9122#[gpui::test]
 9123async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9124    init_test(cx, |_| {});
 9125    let mut cx = EditorLspTestContext::new_rust(
 9126        lsp::ServerCapabilities {
 9127            completion_provider: Some(lsp::CompletionOptions {
 9128                trigger_characters: Some(vec![".".to_string()]),
 9129                ..Default::default()
 9130            }),
 9131            ..Default::default()
 9132        },
 9133        cx,
 9134    )
 9135    .await;
 9136    cx.lsp
 9137        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9138            Ok(Some(lsp::CompletionResponse::Array(vec![
 9139                lsp::CompletionItem {
 9140                    label: "first".into(),
 9141                    ..Default::default()
 9142                },
 9143                lsp::CompletionItem {
 9144                    label: "last".into(),
 9145                    ..Default::default()
 9146                },
 9147            ])))
 9148        });
 9149    cx.set_state("variableˇ");
 9150    cx.simulate_keystroke(".");
 9151    cx.executor().run_until_parked();
 9152
 9153    cx.update_editor(|editor, _, _| {
 9154        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9155        {
 9156            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9157        } else {
 9158            panic!("expected completion menu to be open");
 9159        }
 9160    });
 9161
 9162    cx.update_editor(|editor, window, cx| {
 9163        editor.move_page_down(&MovePageDown::default(), window, cx);
 9164        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9165        {
 9166            assert!(
 9167                menu.selected_item == 1,
 9168                "expected PageDown to select the last item from the context menu"
 9169            );
 9170        } else {
 9171            panic!("expected completion menu to stay open after PageDown");
 9172        }
 9173    });
 9174
 9175    cx.update_editor(|editor, window, cx| {
 9176        editor.move_page_up(&MovePageUp::default(), window, cx);
 9177        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9178        {
 9179            assert!(
 9180                menu.selected_item == 0,
 9181                "expected PageUp to select the first item from the context menu"
 9182            );
 9183        } else {
 9184            panic!("expected completion menu to stay open after PageUp");
 9185        }
 9186    });
 9187}
 9188
 9189#[gpui::test]
 9190async fn test_completion_sort(cx: &mut TestAppContext) {
 9191    init_test(cx, |_| {});
 9192    let mut cx = EditorLspTestContext::new_rust(
 9193        lsp::ServerCapabilities {
 9194            completion_provider: Some(lsp::CompletionOptions {
 9195                trigger_characters: Some(vec![".".to_string()]),
 9196                ..Default::default()
 9197            }),
 9198            ..Default::default()
 9199        },
 9200        cx,
 9201    )
 9202    .await;
 9203    cx.lsp
 9204        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9205            Ok(Some(lsp::CompletionResponse::Array(vec![
 9206                lsp::CompletionItem {
 9207                    label: "Range".into(),
 9208                    sort_text: Some("a".into()),
 9209                    ..Default::default()
 9210                },
 9211                lsp::CompletionItem {
 9212                    label: "r".into(),
 9213                    sort_text: Some("b".into()),
 9214                    ..Default::default()
 9215                },
 9216                lsp::CompletionItem {
 9217                    label: "ret".into(),
 9218                    sort_text: Some("c".into()),
 9219                    ..Default::default()
 9220                },
 9221                lsp::CompletionItem {
 9222                    label: "return".into(),
 9223                    sort_text: Some("d".into()),
 9224                    ..Default::default()
 9225                },
 9226                lsp::CompletionItem {
 9227                    label: "slice".into(),
 9228                    sort_text: Some("d".into()),
 9229                    ..Default::default()
 9230                },
 9231            ])))
 9232        });
 9233    cx.set_state("");
 9234    cx.executor().run_until_parked();
 9235    cx.update_editor(|editor, window, cx| {
 9236        editor.show_completions(
 9237            &ShowCompletions {
 9238                trigger: Some("r".into()),
 9239            },
 9240            window,
 9241            cx,
 9242        );
 9243    });
 9244    cx.executor().run_until_parked();
 9245
 9246    cx.update_editor(|editor, _, _| {
 9247        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9248        {
 9249            assert_eq!(
 9250                completion_menu_entries(&menu),
 9251                &["r", "ret", "Range", "return"]
 9252            );
 9253        } else {
 9254            panic!("expected completion menu to be open");
 9255        }
 9256    });
 9257}
 9258
 9259#[gpui::test]
 9260async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9261    init_test(cx, |_| {});
 9262
 9263    let mut cx = EditorLspTestContext::new_rust(
 9264        lsp::ServerCapabilities {
 9265            completion_provider: Some(lsp::CompletionOptions {
 9266                trigger_characters: Some(vec![".".to_string()]),
 9267                resolve_provider: Some(true),
 9268                ..Default::default()
 9269            }),
 9270            ..Default::default()
 9271        },
 9272        cx,
 9273    )
 9274    .await;
 9275
 9276    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9277    cx.simulate_keystroke(".");
 9278    let completion_item = lsp::CompletionItem {
 9279        label: "Some".into(),
 9280        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9281        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9282        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9283            kind: lsp::MarkupKind::Markdown,
 9284            value: "```rust\nSome(2)\n```".to_string(),
 9285        })),
 9286        deprecated: Some(false),
 9287        sort_text: Some("Some".to_string()),
 9288        filter_text: Some("Some".to_string()),
 9289        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9290        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9291            range: lsp::Range {
 9292                start: lsp::Position {
 9293                    line: 0,
 9294                    character: 22,
 9295                },
 9296                end: lsp::Position {
 9297                    line: 0,
 9298                    character: 22,
 9299                },
 9300            },
 9301            new_text: "Some(2)".to_string(),
 9302        })),
 9303        additional_text_edits: Some(vec![lsp::TextEdit {
 9304            range: lsp::Range {
 9305                start: lsp::Position {
 9306                    line: 0,
 9307                    character: 20,
 9308                },
 9309                end: lsp::Position {
 9310                    line: 0,
 9311                    character: 22,
 9312                },
 9313            },
 9314            new_text: "".to_string(),
 9315        }]),
 9316        ..Default::default()
 9317    };
 9318
 9319    let closure_completion_item = completion_item.clone();
 9320    let counter = Arc::new(AtomicUsize::new(0));
 9321    let counter_clone = counter.clone();
 9322    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9323        let task_completion_item = closure_completion_item.clone();
 9324        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9325        async move {
 9326            Ok(Some(lsp::CompletionResponse::Array(vec![
 9327                task_completion_item,
 9328            ])))
 9329        }
 9330    });
 9331
 9332    cx.condition(|editor, _| editor.context_menu_visible())
 9333        .await;
 9334    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9335    assert!(request.next().await.is_some());
 9336    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9337
 9338    cx.simulate_keystroke("S");
 9339    cx.simulate_keystroke("o");
 9340    cx.simulate_keystroke("m");
 9341    cx.condition(|editor, _| editor.context_menu_visible())
 9342        .await;
 9343    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9344    assert!(request.next().await.is_some());
 9345    assert!(request.next().await.is_some());
 9346    assert!(request.next().await.is_some());
 9347    request.close();
 9348    assert!(request.next().await.is_none());
 9349    assert_eq!(
 9350        counter.load(atomic::Ordering::Acquire),
 9351        4,
 9352        "With the completions menu open, only one LSP request should happen per input"
 9353    );
 9354}
 9355
 9356#[gpui::test]
 9357async fn test_toggle_comment(cx: &mut TestAppContext) {
 9358    init_test(cx, |_| {});
 9359    let mut cx = EditorTestContext::new(cx).await;
 9360    let language = Arc::new(Language::new(
 9361        LanguageConfig {
 9362            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9363            ..Default::default()
 9364        },
 9365        Some(tree_sitter_rust::LANGUAGE.into()),
 9366    ));
 9367    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9368
 9369    // If multiple selections intersect a line, the line is only toggled once.
 9370    cx.set_state(indoc! {"
 9371        fn a() {
 9372            «//b();
 9373            ˇ»// «c();
 9374            //ˇ»  d();
 9375        }
 9376    "});
 9377
 9378    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9379
 9380    cx.assert_editor_state(indoc! {"
 9381        fn a() {
 9382            «b();
 9383            c();
 9384            ˇ» d();
 9385        }
 9386    "});
 9387
 9388    // The comment prefix is inserted at the same column for every line in a
 9389    // selection.
 9390    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9391
 9392    cx.assert_editor_state(indoc! {"
 9393        fn a() {
 9394            // «b();
 9395            // c();
 9396            ˇ»//  d();
 9397        }
 9398    "});
 9399
 9400    // If a selection ends at the beginning of a line, that line is not toggled.
 9401    cx.set_selections_state(indoc! {"
 9402        fn a() {
 9403            // b();
 9404            «// c();
 9405        ˇ»    //  d();
 9406        }
 9407    "});
 9408
 9409    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9410
 9411    cx.assert_editor_state(indoc! {"
 9412        fn a() {
 9413            // b();
 9414            «c();
 9415        ˇ»    //  d();
 9416        }
 9417    "});
 9418
 9419    // If a selection span a single line and is empty, the line is toggled.
 9420    cx.set_state(indoc! {"
 9421        fn a() {
 9422            a();
 9423            b();
 9424        ˇ
 9425        }
 9426    "});
 9427
 9428    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9429
 9430    cx.assert_editor_state(indoc! {"
 9431        fn a() {
 9432            a();
 9433            b();
 9434        //•ˇ
 9435        }
 9436    "});
 9437
 9438    // If a selection span multiple lines, empty lines are not toggled.
 9439    cx.set_state(indoc! {"
 9440        fn a() {
 9441            «a();
 9442
 9443            c();ˇ»
 9444        }
 9445    "});
 9446
 9447    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9448
 9449    cx.assert_editor_state(indoc! {"
 9450        fn a() {
 9451            // «a();
 9452
 9453            // c();ˇ»
 9454        }
 9455    "});
 9456
 9457    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9458    cx.set_state(indoc! {"
 9459        fn a() {
 9460            «// a();
 9461            /// b();
 9462            //! c();ˇ»
 9463        }
 9464    "});
 9465
 9466    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9467
 9468    cx.assert_editor_state(indoc! {"
 9469        fn a() {
 9470            «a();
 9471            b();
 9472            c();ˇ»
 9473        }
 9474    "});
 9475}
 9476
 9477#[gpui::test]
 9478async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9479    init_test(cx, |_| {});
 9480    let mut cx = EditorTestContext::new(cx).await;
 9481    let language = Arc::new(Language::new(
 9482        LanguageConfig {
 9483            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9484            ..Default::default()
 9485        },
 9486        Some(tree_sitter_rust::LANGUAGE.into()),
 9487    ));
 9488    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9489
 9490    let toggle_comments = &ToggleComments {
 9491        advance_downwards: false,
 9492        ignore_indent: true,
 9493    };
 9494
 9495    // If multiple selections intersect a line, the line is only toggled once.
 9496    cx.set_state(indoc! {"
 9497        fn a() {
 9498        //    «b();
 9499        //    c();
 9500        //    ˇ» d();
 9501        }
 9502    "});
 9503
 9504    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9505
 9506    cx.assert_editor_state(indoc! {"
 9507        fn a() {
 9508            «b();
 9509            c();
 9510            ˇ» d();
 9511        }
 9512    "});
 9513
 9514    // The comment prefix is inserted at the beginning of each line
 9515    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9516
 9517    cx.assert_editor_state(indoc! {"
 9518        fn a() {
 9519        //    «b();
 9520        //    c();
 9521        //    ˇ» d();
 9522        }
 9523    "});
 9524
 9525    // If a selection ends at the beginning of a line, that line is not toggled.
 9526    cx.set_selections_state(indoc! {"
 9527        fn a() {
 9528        //    b();
 9529        //    «c();
 9530        ˇ»//     d();
 9531        }
 9532    "});
 9533
 9534    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9535
 9536    cx.assert_editor_state(indoc! {"
 9537        fn a() {
 9538        //    b();
 9539            «c();
 9540        ˇ»//     d();
 9541        }
 9542    "});
 9543
 9544    // If a selection span a single line and is empty, the line is toggled.
 9545    cx.set_state(indoc! {"
 9546        fn a() {
 9547            a();
 9548            b();
 9549        ˇ
 9550        }
 9551    "});
 9552
 9553    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9554
 9555    cx.assert_editor_state(indoc! {"
 9556        fn a() {
 9557            a();
 9558            b();
 9559        //ˇ
 9560        }
 9561    "});
 9562
 9563    // If a selection span multiple lines, empty lines are not toggled.
 9564    cx.set_state(indoc! {"
 9565        fn a() {
 9566            «a();
 9567
 9568            c();ˇ»
 9569        }
 9570    "});
 9571
 9572    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9573
 9574    cx.assert_editor_state(indoc! {"
 9575        fn a() {
 9576        //    «a();
 9577
 9578        //    c();ˇ»
 9579        }
 9580    "});
 9581
 9582    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9583    cx.set_state(indoc! {"
 9584        fn a() {
 9585        //    «a();
 9586        ///    b();
 9587        //!    c();ˇ»
 9588        }
 9589    "});
 9590
 9591    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9592
 9593    cx.assert_editor_state(indoc! {"
 9594        fn a() {
 9595            «a();
 9596            b();
 9597            c();ˇ»
 9598        }
 9599    "});
 9600}
 9601
 9602#[gpui::test]
 9603async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9604    init_test(cx, |_| {});
 9605
 9606    let language = Arc::new(Language::new(
 9607        LanguageConfig {
 9608            line_comments: vec!["// ".into()],
 9609            ..Default::default()
 9610        },
 9611        Some(tree_sitter_rust::LANGUAGE.into()),
 9612    ));
 9613
 9614    let mut cx = EditorTestContext::new(cx).await;
 9615
 9616    cx.language_registry().add(language.clone());
 9617    cx.update_buffer(|buffer, cx| {
 9618        buffer.set_language(Some(language), cx);
 9619    });
 9620
 9621    let toggle_comments = &ToggleComments {
 9622        advance_downwards: true,
 9623        ignore_indent: false,
 9624    };
 9625
 9626    // Single cursor on one line -> advance
 9627    // Cursor moves horizontally 3 characters as well on non-blank line
 9628    cx.set_state(indoc!(
 9629        "fn a() {
 9630             ˇdog();
 9631             cat();
 9632        }"
 9633    ));
 9634    cx.update_editor(|editor, window, cx| {
 9635        editor.toggle_comments(toggle_comments, window, cx);
 9636    });
 9637    cx.assert_editor_state(indoc!(
 9638        "fn a() {
 9639             // dog();
 9640             catˇ();
 9641        }"
 9642    ));
 9643
 9644    // Single selection on one line -> don't advance
 9645    cx.set_state(indoc!(
 9646        "fn a() {
 9647             «dog()ˇ»;
 9648             cat();
 9649        }"
 9650    ));
 9651    cx.update_editor(|editor, window, cx| {
 9652        editor.toggle_comments(toggle_comments, window, cx);
 9653    });
 9654    cx.assert_editor_state(indoc!(
 9655        "fn a() {
 9656             // «dog()ˇ»;
 9657             cat();
 9658        }"
 9659    ));
 9660
 9661    // Multiple cursors on one line -> advance
 9662    cx.set_state(indoc!(
 9663        "fn a() {
 9664             ˇdˇog();
 9665             cat();
 9666        }"
 9667    ));
 9668    cx.update_editor(|editor, window, cx| {
 9669        editor.toggle_comments(toggle_comments, window, cx);
 9670    });
 9671    cx.assert_editor_state(indoc!(
 9672        "fn a() {
 9673             // dog();
 9674             catˇ(ˇ);
 9675        }"
 9676    ));
 9677
 9678    // Multiple cursors on one line, with selection -> don't advance
 9679    cx.set_state(indoc!(
 9680        "fn a() {
 9681             ˇdˇog«()ˇ»;
 9682             cat();
 9683        }"
 9684    ));
 9685    cx.update_editor(|editor, window, cx| {
 9686        editor.toggle_comments(toggle_comments, window, cx);
 9687    });
 9688    cx.assert_editor_state(indoc!(
 9689        "fn a() {
 9690             // ˇdˇog«()ˇ»;
 9691             cat();
 9692        }"
 9693    ));
 9694
 9695    // Single cursor on one line -> advance
 9696    // Cursor moves to column 0 on blank line
 9697    cx.set_state(indoc!(
 9698        "fn a() {
 9699             ˇdog();
 9700
 9701             cat();
 9702        }"
 9703    ));
 9704    cx.update_editor(|editor, window, cx| {
 9705        editor.toggle_comments(toggle_comments, window, cx);
 9706    });
 9707    cx.assert_editor_state(indoc!(
 9708        "fn a() {
 9709             // dog();
 9710        ˇ
 9711             cat();
 9712        }"
 9713    ));
 9714
 9715    // Single cursor on one line -> advance
 9716    // Cursor starts and ends at column 0
 9717    cx.set_state(indoc!(
 9718        "fn a() {
 9719         ˇ    dog();
 9720             cat();
 9721        }"
 9722    ));
 9723    cx.update_editor(|editor, window, cx| {
 9724        editor.toggle_comments(toggle_comments, window, cx);
 9725    });
 9726    cx.assert_editor_state(indoc!(
 9727        "fn a() {
 9728             // dog();
 9729         ˇ    cat();
 9730        }"
 9731    ));
 9732}
 9733
 9734#[gpui::test]
 9735async fn test_toggle_block_comment(cx: &mut TestAppContext) {
 9736    init_test(cx, |_| {});
 9737
 9738    let mut cx = EditorTestContext::new(cx).await;
 9739
 9740    let html_language = Arc::new(
 9741        Language::new(
 9742            LanguageConfig {
 9743                name: "HTML".into(),
 9744                block_comment: Some(("<!-- ".into(), " -->".into())),
 9745                ..Default::default()
 9746            },
 9747            Some(tree_sitter_html::LANGUAGE.into()),
 9748        )
 9749        .with_injection_query(
 9750            r#"
 9751            (script_element
 9752                (raw_text) @injection.content
 9753                (#set! injection.language "javascript"))
 9754            "#,
 9755        )
 9756        .unwrap(),
 9757    );
 9758
 9759    let javascript_language = Arc::new(Language::new(
 9760        LanguageConfig {
 9761            name: "JavaScript".into(),
 9762            line_comments: vec!["// ".into()],
 9763            ..Default::default()
 9764        },
 9765        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9766    ));
 9767
 9768    cx.language_registry().add(html_language.clone());
 9769    cx.language_registry().add(javascript_language.clone());
 9770    cx.update_buffer(|buffer, cx| {
 9771        buffer.set_language(Some(html_language), cx);
 9772    });
 9773
 9774    // Toggle comments for empty selections
 9775    cx.set_state(
 9776        &r#"
 9777            <p>A</p>ˇ
 9778            <p>B</p>ˇ
 9779            <p>C</p>ˇ
 9780        "#
 9781        .unindent(),
 9782    );
 9783    cx.update_editor(|editor, window, cx| {
 9784        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9785    });
 9786    cx.assert_editor_state(
 9787        &r#"
 9788            <!-- <p>A</p>ˇ -->
 9789            <!-- <p>B</p>ˇ -->
 9790            <!-- <p>C</p>ˇ -->
 9791        "#
 9792        .unindent(),
 9793    );
 9794    cx.update_editor(|editor, window, cx| {
 9795        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9796    });
 9797    cx.assert_editor_state(
 9798        &r#"
 9799            <p>A</p>ˇ
 9800            <p>B</p>ˇ
 9801            <p>C</p>ˇ
 9802        "#
 9803        .unindent(),
 9804    );
 9805
 9806    // Toggle comments for mixture of empty and non-empty selections, where
 9807    // multiple selections occupy a given line.
 9808    cx.set_state(
 9809        &r#"
 9810            <p>A«</p>
 9811            <p>ˇ»B</p>ˇ
 9812            <p>C«</p>
 9813            <p>ˇ»D</p>ˇ
 9814        "#
 9815        .unindent(),
 9816    );
 9817
 9818    cx.update_editor(|editor, window, cx| {
 9819        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9820    });
 9821    cx.assert_editor_state(
 9822        &r#"
 9823            <!-- <p>A«</p>
 9824            <p>ˇ»B</p>ˇ -->
 9825            <!-- <p>C«</p>
 9826            <p>ˇ»D</p>ˇ -->
 9827        "#
 9828        .unindent(),
 9829    );
 9830    cx.update_editor(|editor, window, cx| {
 9831        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9832    });
 9833    cx.assert_editor_state(
 9834        &r#"
 9835            <p>A«</p>
 9836            <p>ˇ»B</p>ˇ
 9837            <p>C«</p>
 9838            <p>ˇ»D</p>ˇ
 9839        "#
 9840        .unindent(),
 9841    );
 9842
 9843    // Toggle comments when different languages are active for different
 9844    // selections.
 9845    cx.set_state(
 9846        &r#"
 9847            ˇ<script>
 9848                ˇvar x = new Y();
 9849            ˇ</script>
 9850        "#
 9851        .unindent(),
 9852    );
 9853    cx.executor().run_until_parked();
 9854    cx.update_editor(|editor, window, cx| {
 9855        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9856    });
 9857    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9858    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9859    cx.assert_editor_state(
 9860        &r#"
 9861            <!-- ˇ<script> -->
 9862                // ˇvar x = new Y();
 9863            <!-- ˇ</script> -->
 9864        "#
 9865        .unindent(),
 9866    );
 9867}
 9868
 9869#[gpui::test]
 9870fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9871    init_test(cx, |_| {});
 9872
 9873    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9874    let multibuffer = cx.new(|cx| {
 9875        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9876        multibuffer.push_excerpts(
 9877            buffer.clone(),
 9878            [
 9879                ExcerptRange {
 9880                    context: Point::new(0, 0)..Point::new(0, 4),
 9881                    primary: None,
 9882                },
 9883                ExcerptRange {
 9884                    context: Point::new(1, 0)..Point::new(1, 4),
 9885                    primary: None,
 9886                },
 9887            ],
 9888            cx,
 9889        );
 9890        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9891        multibuffer
 9892    });
 9893
 9894    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9895    editor.update_in(cx, |editor, window, cx| {
 9896        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9897        editor.change_selections(None, window, cx, |s| {
 9898            s.select_ranges([
 9899                Point::new(0, 0)..Point::new(0, 0),
 9900                Point::new(1, 0)..Point::new(1, 0),
 9901            ])
 9902        });
 9903
 9904        editor.handle_input("X", window, cx);
 9905        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9906        assert_eq!(
 9907            editor.selections.ranges(cx),
 9908            [
 9909                Point::new(0, 1)..Point::new(0, 1),
 9910                Point::new(1, 1)..Point::new(1, 1),
 9911            ]
 9912        );
 9913
 9914        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9915        editor.change_selections(None, window, cx, |s| {
 9916            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9917        });
 9918        editor.backspace(&Default::default(), window, cx);
 9919        assert_eq!(editor.text(cx), "Xa\nbbb");
 9920        assert_eq!(
 9921            editor.selections.ranges(cx),
 9922            [Point::new(1, 0)..Point::new(1, 0)]
 9923        );
 9924
 9925        editor.change_selections(None, window, cx, |s| {
 9926            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9927        });
 9928        editor.backspace(&Default::default(), window, cx);
 9929        assert_eq!(editor.text(cx), "X\nbb");
 9930        assert_eq!(
 9931            editor.selections.ranges(cx),
 9932            [Point::new(0, 1)..Point::new(0, 1)]
 9933        );
 9934    });
 9935}
 9936
 9937#[gpui::test]
 9938fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9939    init_test(cx, |_| {});
 9940
 9941    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9942    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9943        indoc! {"
 9944            [aaaa
 9945            (bbbb]
 9946            cccc)",
 9947        },
 9948        markers.clone(),
 9949    );
 9950    let excerpt_ranges = markers.into_iter().map(|marker| {
 9951        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9952        ExcerptRange {
 9953            context,
 9954            primary: None,
 9955        }
 9956    });
 9957    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9958    let multibuffer = cx.new(|cx| {
 9959        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9960        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9961        multibuffer
 9962    });
 9963
 9964    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9965    editor.update_in(cx, |editor, window, cx| {
 9966        let (expected_text, selection_ranges) = marked_text_ranges(
 9967            indoc! {"
 9968                aaaa
 9969                bˇbbb
 9970                bˇbbˇb
 9971                cccc"
 9972            },
 9973            true,
 9974        );
 9975        assert_eq!(editor.text(cx), expected_text);
 9976        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9977
 9978        editor.handle_input("X", window, cx);
 9979
 9980        let (expected_text, expected_selections) = marked_text_ranges(
 9981            indoc! {"
 9982                aaaa
 9983                bXˇbbXb
 9984                bXˇbbXˇb
 9985                cccc"
 9986            },
 9987            false,
 9988        );
 9989        assert_eq!(editor.text(cx), expected_text);
 9990        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9991
 9992        editor.newline(&Newline, window, cx);
 9993        let (expected_text, expected_selections) = marked_text_ranges(
 9994            indoc! {"
 9995                aaaa
 9996                bX
 9997                ˇbbX
 9998                b
 9999                bX
10000                ˇbbX
10001                ˇb
10002                cccc"
10003            },
10004            false,
10005        );
10006        assert_eq!(editor.text(cx), expected_text);
10007        assert_eq!(editor.selections.ranges(cx), expected_selections);
10008    });
10009}
10010
10011#[gpui::test]
10012fn test_refresh_selections(cx: &mut TestAppContext) {
10013    init_test(cx, |_| {});
10014
10015    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10016    let mut excerpt1_id = None;
10017    let multibuffer = cx.new(|cx| {
10018        let mut multibuffer = MultiBuffer::new(ReadWrite);
10019        excerpt1_id = multibuffer
10020            .push_excerpts(
10021                buffer.clone(),
10022                [
10023                    ExcerptRange {
10024                        context: Point::new(0, 0)..Point::new(1, 4),
10025                        primary: None,
10026                    },
10027                    ExcerptRange {
10028                        context: Point::new(1, 0)..Point::new(2, 4),
10029                        primary: None,
10030                    },
10031                ],
10032                cx,
10033            )
10034            .into_iter()
10035            .next();
10036        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10037        multibuffer
10038    });
10039
10040    let editor = cx.add_window(|window, cx| {
10041        let mut editor = build_editor(multibuffer.clone(), window, cx);
10042        let snapshot = editor.snapshot(window, cx);
10043        editor.change_selections(None, window, cx, |s| {
10044            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10045        });
10046        editor.begin_selection(
10047            Point::new(2, 1).to_display_point(&snapshot),
10048            true,
10049            1,
10050            window,
10051            cx,
10052        );
10053        assert_eq!(
10054            editor.selections.ranges(cx),
10055            [
10056                Point::new(1, 3)..Point::new(1, 3),
10057                Point::new(2, 1)..Point::new(2, 1),
10058            ]
10059        );
10060        editor
10061    });
10062
10063    // Refreshing selections is a no-op when excerpts haven't changed.
10064    _ = editor.update(cx, |editor, window, cx| {
10065        editor.change_selections(None, window, cx, |s| s.refresh());
10066        assert_eq!(
10067            editor.selections.ranges(cx),
10068            [
10069                Point::new(1, 3)..Point::new(1, 3),
10070                Point::new(2, 1)..Point::new(2, 1),
10071            ]
10072        );
10073    });
10074
10075    multibuffer.update(cx, |multibuffer, cx| {
10076        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10077    });
10078    _ = editor.update(cx, |editor, window, cx| {
10079        // Removing an excerpt causes the first selection to become degenerate.
10080        assert_eq!(
10081            editor.selections.ranges(cx),
10082            [
10083                Point::new(0, 0)..Point::new(0, 0),
10084                Point::new(0, 1)..Point::new(0, 1)
10085            ]
10086        );
10087
10088        // Refreshing selections will relocate the first selection to the original buffer
10089        // location.
10090        editor.change_selections(None, window, cx, |s| s.refresh());
10091        assert_eq!(
10092            editor.selections.ranges(cx),
10093            [
10094                Point::new(0, 1)..Point::new(0, 1),
10095                Point::new(0, 3)..Point::new(0, 3)
10096            ]
10097        );
10098        assert!(editor.selections.pending_anchor().is_some());
10099    });
10100}
10101
10102#[gpui::test]
10103fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10104    init_test(cx, |_| {});
10105
10106    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10107    let mut excerpt1_id = None;
10108    let multibuffer = cx.new(|cx| {
10109        let mut multibuffer = MultiBuffer::new(ReadWrite);
10110        excerpt1_id = multibuffer
10111            .push_excerpts(
10112                buffer.clone(),
10113                [
10114                    ExcerptRange {
10115                        context: Point::new(0, 0)..Point::new(1, 4),
10116                        primary: None,
10117                    },
10118                    ExcerptRange {
10119                        context: Point::new(1, 0)..Point::new(2, 4),
10120                        primary: None,
10121                    },
10122                ],
10123                cx,
10124            )
10125            .into_iter()
10126            .next();
10127        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10128        multibuffer
10129    });
10130
10131    let editor = cx.add_window(|window, cx| {
10132        let mut editor = build_editor(multibuffer.clone(), window, cx);
10133        let snapshot = editor.snapshot(window, cx);
10134        editor.begin_selection(
10135            Point::new(1, 3).to_display_point(&snapshot),
10136            false,
10137            1,
10138            window,
10139            cx,
10140        );
10141        assert_eq!(
10142            editor.selections.ranges(cx),
10143            [Point::new(1, 3)..Point::new(1, 3)]
10144        );
10145        editor
10146    });
10147
10148    multibuffer.update(cx, |multibuffer, cx| {
10149        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10150    });
10151    _ = editor.update(cx, |editor, window, cx| {
10152        assert_eq!(
10153            editor.selections.ranges(cx),
10154            [Point::new(0, 0)..Point::new(0, 0)]
10155        );
10156
10157        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10158        editor.change_selections(None, window, cx, |s| s.refresh());
10159        assert_eq!(
10160            editor.selections.ranges(cx),
10161            [Point::new(0, 3)..Point::new(0, 3)]
10162        );
10163        assert!(editor.selections.pending_anchor().is_some());
10164    });
10165}
10166
10167#[gpui::test]
10168async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10169    init_test(cx, |_| {});
10170
10171    let language = Arc::new(
10172        Language::new(
10173            LanguageConfig {
10174                brackets: BracketPairConfig {
10175                    pairs: vec![
10176                        BracketPair {
10177                            start: "{".to_string(),
10178                            end: "}".to_string(),
10179                            close: true,
10180                            surround: true,
10181                            newline: true,
10182                        },
10183                        BracketPair {
10184                            start: "/* ".to_string(),
10185                            end: " */".to_string(),
10186                            close: true,
10187                            surround: true,
10188                            newline: true,
10189                        },
10190                    ],
10191                    ..Default::default()
10192                },
10193                ..Default::default()
10194            },
10195            Some(tree_sitter_rust::LANGUAGE.into()),
10196        )
10197        .with_indents_query("")
10198        .unwrap(),
10199    );
10200
10201    let text = concat!(
10202        "{   }\n",     //
10203        "  x\n",       //
10204        "  /*   */\n", //
10205        "x\n",         //
10206        "{{} }\n",     //
10207    );
10208
10209    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10210    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10211    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10212    editor
10213        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10214        .await;
10215
10216    editor.update_in(cx, |editor, window, cx| {
10217        editor.change_selections(None, window, cx, |s| {
10218            s.select_display_ranges([
10219                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10220                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10221                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10222            ])
10223        });
10224        editor.newline(&Newline, window, cx);
10225
10226        assert_eq!(
10227            editor.buffer().read(cx).read(cx).text(),
10228            concat!(
10229                "{ \n",    // Suppress rustfmt
10230                "\n",      //
10231                "}\n",     //
10232                "  x\n",   //
10233                "  /* \n", //
10234                "  \n",    //
10235                "  */\n",  //
10236                "x\n",     //
10237                "{{} \n",  //
10238                "}\n",     //
10239            )
10240        );
10241    });
10242}
10243
10244#[gpui::test]
10245fn test_highlighted_ranges(cx: &mut TestAppContext) {
10246    init_test(cx, |_| {});
10247
10248    let editor = cx.add_window(|window, cx| {
10249        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10250        build_editor(buffer.clone(), window, cx)
10251    });
10252
10253    _ = editor.update(cx, |editor, window, cx| {
10254        struct Type1;
10255        struct Type2;
10256
10257        let buffer = editor.buffer.read(cx).snapshot(cx);
10258
10259        let anchor_range =
10260            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10261
10262        editor.highlight_background::<Type1>(
10263            &[
10264                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10265                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10266                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10267                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10268            ],
10269            |_| Hsla::red(),
10270            cx,
10271        );
10272        editor.highlight_background::<Type2>(
10273            &[
10274                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10275                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10276                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10277                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10278            ],
10279            |_| Hsla::green(),
10280            cx,
10281        );
10282
10283        let snapshot = editor.snapshot(window, cx);
10284        let mut highlighted_ranges = editor.background_highlights_in_range(
10285            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10286            &snapshot,
10287            cx.theme().colors(),
10288        );
10289        // Enforce a consistent ordering based on color without relying on the ordering of the
10290        // highlight's `TypeId` which is non-executor.
10291        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10292        assert_eq!(
10293            highlighted_ranges,
10294            &[
10295                (
10296                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10297                    Hsla::red(),
10298                ),
10299                (
10300                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10301                    Hsla::red(),
10302                ),
10303                (
10304                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10305                    Hsla::green(),
10306                ),
10307                (
10308                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10309                    Hsla::green(),
10310                ),
10311            ]
10312        );
10313        assert_eq!(
10314            editor.background_highlights_in_range(
10315                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10316                &snapshot,
10317                cx.theme().colors(),
10318            ),
10319            &[(
10320                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10321                Hsla::red(),
10322            )]
10323        );
10324    });
10325}
10326
10327#[gpui::test]
10328async fn test_following(cx: &mut TestAppContext) {
10329    init_test(cx, |_| {});
10330
10331    let fs = FakeFs::new(cx.executor());
10332    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10333
10334    let buffer = project.update(cx, |project, cx| {
10335        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10336        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10337    });
10338    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10339    let follower = cx.update(|cx| {
10340        cx.open_window(
10341            WindowOptions {
10342                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10343                    gpui::Point::new(px(0.), px(0.)),
10344                    gpui::Point::new(px(10.), px(80.)),
10345                ))),
10346                ..Default::default()
10347            },
10348            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10349        )
10350        .unwrap()
10351    });
10352
10353    let is_still_following = Rc::new(RefCell::new(true));
10354    let follower_edit_event_count = Rc::new(RefCell::new(0));
10355    let pending_update = Rc::new(RefCell::new(None));
10356    let leader_entity = leader.root(cx).unwrap();
10357    let follower_entity = follower.root(cx).unwrap();
10358    _ = follower.update(cx, {
10359        let update = pending_update.clone();
10360        let is_still_following = is_still_following.clone();
10361        let follower_edit_event_count = follower_edit_event_count.clone();
10362        |_, window, cx| {
10363            cx.subscribe_in(
10364                &leader_entity,
10365                window,
10366                move |_, leader, event, window, cx| {
10367                    leader.read(cx).add_event_to_update_proto(
10368                        event,
10369                        &mut update.borrow_mut(),
10370                        window,
10371                        cx,
10372                    );
10373                },
10374            )
10375            .detach();
10376
10377            cx.subscribe_in(
10378                &follower_entity,
10379                window,
10380                move |_, _, event: &EditorEvent, _window, _cx| {
10381                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10382                        *is_still_following.borrow_mut() = false;
10383                    }
10384
10385                    if let EditorEvent::BufferEdited = event {
10386                        *follower_edit_event_count.borrow_mut() += 1;
10387                    }
10388                },
10389            )
10390            .detach();
10391        }
10392    });
10393
10394    // Update the selections only
10395    _ = leader.update(cx, |leader, window, cx| {
10396        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10397    });
10398    follower
10399        .update(cx, |follower, window, cx| {
10400            follower.apply_update_proto(
10401                &project,
10402                pending_update.borrow_mut().take().unwrap(),
10403                window,
10404                cx,
10405            )
10406        })
10407        .unwrap()
10408        .await
10409        .unwrap();
10410    _ = follower.update(cx, |follower, _, cx| {
10411        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10412    });
10413    assert!(*is_still_following.borrow());
10414    assert_eq!(*follower_edit_event_count.borrow(), 0);
10415
10416    // Update the scroll position only
10417    _ = leader.update(cx, |leader, window, cx| {
10418        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10419    });
10420    follower
10421        .update(cx, |follower, window, cx| {
10422            follower.apply_update_proto(
10423                &project,
10424                pending_update.borrow_mut().take().unwrap(),
10425                window,
10426                cx,
10427            )
10428        })
10429        .unwrap()
10430        .await
10431        .unwrap();
10432    assert_eq!(
10433        follower
10434            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10435            .unwrap(),
10436        gpui::Point::new(1.5, 3.5)
10437    );
10438    assert!(*is_still_following.borrow());
10439    assert_eq!(*follower_edit_event_count.borrow(), 0);
10440
10441    // Update the selections and scroll position. The follower's scroll position is updated
10442    // via autoscroll, not via the leader's exact scroll position.
10443    _ = leader.update(cx, |leader, window, cx| {
10444        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10445        leader.request_autoscroll(Autoscroll::newest(), cx);
10446        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10447    });
10448    follower
10449        .update(cx, |follower, window, cx| {
10450            follower.apply_update_proto(
10451                &project,
10452                pending_update.borrow_mut().take().unwrap(),
10453                window,
10454                cx,
10455            )
10456        })
10457        .unwrap()
10458        .await
10459        .unwrap();
10460    _ = follower.update(cx, |follower, _, cx| {
10461        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10462        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10463    });
10464    assert!(*is_still_following.borrow());
10465
10466    // Creating a pending selection that precedes another selection
10467    _ = leader.update(cx, |leader, window, cx| {
10468        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10469        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10470    });
10471    follower
10472        .update(cx, |follower, window, cx| {
10473            follower.apply_update_proto(
10474                &project,
10475                pending_update.borrow_mut().take().unwrap(),
10476                window,
10477                cx,
10478            )
10479        })
10480        .unwrap()
10481        .await
10482        .unwrap();
10483    _ = follower.update(cx, |follower, _, cx| {
10484        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10485    });
10486    assert!(*is_still_following.borrow());
10487
10488    // Extend the pending selection so that it surrounds another selection
10489    _ = leader.update(cx, |leader, window, cx| {
10490        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10491    });
10492    follower
10493        .update(cx, |follower, window, cx| {
10494            follower.apply_update_proto(
10495                &project,
10496                pending_update.borrow_mut().take().unwrap(),
10497                window,
10498                cx,
10499            )
10500        })
10501        .unwrap()
10502        .await
10503        .unwrap();
10504    _ = follower.update(cx, |follower, _, cx| {
10505        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10506    });
10507
10508    // Scrolling locally breaks the follow
10509    _ = follower.update(cx, |follower, window, cx| {
10510        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10511        follower.set_scroll_anchor(
10512            ScrollAnchor {
10513                anchor: top_anchor,
10514                offset: gpui::Point::new(0.0, 0.5),
10515            },
10516            window,
10517            cx,
10518        );
10519    });
10520    assert!(!(*is_still_following.borrow()));
10521}
10522
10523#[gpui::test]
10524async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10525    init_test(cx, |_| {});
10526
10527    let fs = FakeFs::new(cx.executor());
10528    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10529    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10530    let pane = workspace
10531        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10532        .unwrap();
10533
10534    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10535
10536    let leader = pane.update_in(cx, |_, window, cx| {
10537        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10538        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10539    });
10540
10541    // Start following the editor when it has no excerpts.
10542    let mut state_message =
10543        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10544    let workspace_entity = workspace.root(cx).unwrap();
10545    let follower_1 = cx
10546        .update_window(*workspace.deref(), |_, window, cx| {
10547            Editor::from_state_proto(
10548                workspace_entity,
10549                ViewId {
10550                    creator: Default::default(),
10551                    id: 0,
10552                },
10553                &mut state_message,
10554                window,
10555                cx,
10556            )
10557        })
10558        .unwrap()
10559        .unwrap()
10560        .await
10561        .unwrap();
10562
10563    let update_message = Rc::new(RefCell::new(None));
10564    follower_1.update_in(cx, {
10565        let update = update_message.clone();
10566        |_, window, cx| {
10567            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10568                leader.read(cx).add_event_to_update_proto(
10569                    event,
10570                    &mut update.borrow_mut(),
10571                    window,
10572                    cx,
10573                );
10574            })
10575            .detach();
10576        }
10577    });
10578
10579    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10580        (
10581            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10582            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10583        )
10584    });
10585
10586    // Insert some excerpts.
10587    leader.update(cx, |leader, cx| {
10588        leader.buffer.update(cx, |multibuffer, cx| {
10589            let excerpt_ids = multibuffer.push_excerpts(
10590                buffer_1.clone(),
10591                [
10592                    ExcerptRange {
10593                        context: 1..6,
10594                        primary: None,
10595                    },
10596                    ExcerptRange {
10597                        context: 12..15,
10598                        primary: None,
10599                    },
10600                    ExcerptRange {
10601                        context: 0..3,
10602                        primary: None,
10603                    },
10604                ],
10605                cx,
10606            );
10607            multibuffer.insert_excerpts_after(
10608                excerpt_ids[0],
10609                buffer_2.clone(),
10610                [
10611                    ExcerptRange {
10612                        context: 8..12,
10613                        primary: None,
10614                    },
10615                    ExcerptRange {
10616                        context: 0..6,
10617                        primary: None,
10618                    },
10619                ],
10620                cx,
10621            );
10622        });
10623    });
10624
10625    // Apply the update of adding the excerpts.
10626    follower_1
10627        .update_in(cx, |follower, window, cx| {
10628            follower.apply_update_proto(
10629                &project,
10630                update_message.borrow().clone().unwrap(),
10631                window,
10632                cx,
10633            )
10634        })
10635        .await
10636        .unwrap();
10637    assert_eq!(
10638        follower_1.update(cx, |editor, cx| editor.text(cx)),
10639        leader.update(cx, |editor, cx| editor.text(cx))
10640    );
10641    update_message.borrow_mut().take();
10642
10643    // Start following separately after it already has excerpts.
10644    let mut state_message =
10645        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10646    let workspace_entity = workspace.root(cx).unwrap();
10647    let follower_2 = cx
10648        .update_window(*workspace.deref(), |_, window, cx| {
10649            Editor::from_state_proto(
10650                workspace_entity,
10651                ViewId {
10652                    creator: Default::default(),
10653                    id: 0,
10654                },
10655                &mut state_message,
10656                window,
10657                cx,
10658            )
10659        })
10660        .unwrap()
10661        .unwrap()
10662        .await
10663        .unwrap();
10664    assert_eq!(
10665        follower_2.update(cx, |editor, cx| editor.text(cx)),
10666        leader.update(cx, |editor, cx| editor.text(cx))
10667    );
10668
10669    // Remove some excerpts.
10670    leader.update(cx, |leader, cx| {
10671        leader.buffer.update(cx, |multibuffer, cx| {
10672            let excerpt_ids = multibuffer.excerpt_ids();
10673            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10674            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10675        });
10676    });
10677
10678    // Apply the update of removing the excerpts.
10679    follower_1
10680        .update_in(cx, |follower, window, cx| {
10681            follower.apply_update_proto(
10682                &project,
10683                update_message.borrow().clone().unwrap(),
10684                window,
10685                cx,
10686            )
10687        })
10688        .await
10689        .unwrap();
10690    follower_2
10691        .update_in(cx, |follower, window, cx| {
10692            follower.apply_update_proto(
10693                &project,
10694                update_message.borrow().clone().unwrap(),
10695                window,
10696                cx,
10697            )
10698        })
10699        .await
10700        .unwrap();
10701    update_message.borrow_mut().take();
10702    assert_eq!(
10703        follower_1.update(cx, |editor, cx| editor.text(cx)),
10704        leader.update(cx, |editor, cx| editor.text(cx))
10705    );
10706}
10707
10708#[gpui::test]
10709async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
10710    init_test(cx, |_| {});
10711
10712    let mut cx = EditorTestContext::new(cx).await;
10713    let lsp_store =
10714        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10715
10716    cx.set_state(indoc! {"
10717        ˇfn func(abc def: i32) -> u32 {
10718        }
10719    "});
10720
10721    cx.update(|_, cx| {
10722        lsp_store.update(cx, |lsp_store, cx| {
10723            lsp_store
10724                .update_diagnostics(
10725                    LanguageServerId(0),
10726                    lsp::PublishDiagnosticsParams {
10727                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10728                        version: None,
10729                        diagnostics: vec![
10730                            lsp::Diagnostic {
10731                                range: lsp::Range::new(
10732                                    lsp::Position::new(0, 11),
10733                                    lsp::Position::new(0, 12),
10734                                ),
10735                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10736                                ..Default::default()
10737                            },
10738                            lsp::Diagnostic {
10739                                range: lsp::Range::new(
10740                                    lsp::Position::new(0, 12),
10741                                    lsp::Position::new(0, 15),
10742                                ),
10743                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10744                                ..Default::default()
10745                            },
10746                            lsp::Diagnostic {
10747                                range: lsp::Range::new(
10748                                    lsp::Position::new(0, 25),
10749                                    lsp::Position::new(0, 28),
10750                                ),
10751                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10752                                ..Default::default()
10753                            },
10754                        ],
10755                    },
10756                    &[],
10757                    cx,
10758                )
10759                .unwrap()
10760        });
10761    });
10762
10763    executor.run_until_parked();
10764
10765    cx.update_editor(|editor, window, cx| {
10766        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10767    });
10768
10769    cx.assert_editor_state(indoc! {"
10770        fn func(abc def: i32) -> ˇu32 {
10771        }
10772    "});
10773
10774    cx.update_editor(|editor, window, cx| {
10775        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10776    });
10777
10778    cx.assert_editor_state(indoc! {"
10779        fn func(abc ˇdef: i32) -> u32 {
10780        }
10781    "});
10782
10783    cx.update_editor(|editor, window, cx| {
10784        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10785    });
10786
10787    cx.assert_editor_state(indoc! {"
10788        fn func(abcˇ def: i32) -> u32 {
10789        }
10790    "});
10791
10792    cx.update_editor(|editor, window, cx| {
10793        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10794    });
10795
10796    cx.assert_editor_state(indoc! {"
10797        fn func(abc def: i32) -> ˇu32 {
10798        }
10799    "});
10800}
10801
10802#[gpui::test]
10803async fn cycle_through_same_place_diagnostics(
10804    executor: BackgroundExecutor,
10805    cx: &mut TestAppContext,
10806) {
10807    init_test(cx, |_| {});
10808
10809    let mut cx = EditorTestContext::new(cx).await;
10810    let lsp_store =
10811        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10812
10813    cx.set_state(indoc! {"
10814        ˇfn func(abc def: i32) -> u32 {
10815        }
10816    "});
10817
10818    cx.update(|_, cx| {
10819        lsp_store.update(cx, |lsp_store, cx| {
10820            lsp_store
10821                .update_diagnostics(
10822                    LanguageServerId(0),
10823                    lsp::PublishDiagnosticsParams {
10824                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10825                        version: None,
10826                        diagnostics: vec![
10827                            lsp::Diagnostic {
10828                                range: lsp::Range::new(
10829                                    lsp::Position::new(0, 11),
10830                                    lsp::Position::new(0, 12),
10831                                ),
10832                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10833                                ..Default::default()
10834                            },
10835                            lsp::Diagnostic {
10836                                range: lsp::Range::new(
10837                                    lsp::Position::new(0, 12),
10838                                    lsp::Position::new(0, 15),
10839                                ),
10840                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10841                                ..Default::default()
10842                            },
10843                            lsp::Diagnostic {
10844                                range: lsp::Range::new(
10845                                    lsp::Position::new(0, 12),
10846                                    lsp::Position::new(0, 15),
10847                                ),
10848                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10849                                ..Default::default()
10850                            },
10851                            lsp::Diagnostic {
10852                                range: lsp::Range::new(
10853                                    lsp::Position::new(0, 25),
10854                                    lsp::Position::new(0, 28),
10855                                ),
10856                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10857                                ..Default::default()
10858                            },
10859                        ],
10860                    },
10861                    &[],
10862                    cx,
10863                )
10864                .unwrap()
10865        });
10866    });
10867    executor.run_until_parked();
10868
10869    //// Backward
10870
10871    // Fourth diagnostic
10872    cx.update_editor(|editor, window, cx| {
10873        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10874    });
10875    cx.assert_editor_state(indoc! {"
10876        fn func(abc def: i32) -> ˇu32 {
10877        }
10878    "});
10879
10880    // Third diagnostic
10881    cx.update_editor(|editor, window, cx| {
10882        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10883    });
10884    cx.assert_editor_state(indoc! {"
10885        fn func(abc ˇdef: i32) -> u32 {
10886        }
10887    "});
10888
10889    // Second diagnostic, same place
10890    cx.update_editor(|editor, window, cx| {
10891        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10892    });
10893    cx.assert_editor_state(indoc! {"
10894        fn func(abc ˇdef: i32) -> u32 {
10895        }
10896    "});
10897
10898    // First diagnostic
10899    cx.update_editor(|editor, window, cx| {
10900        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10901    });
10902    cx.assert_editor_state(indoc! {"
10903        fn func(abcˇ def: i32) -> u32 {
10904        }
10905    "});
10906
10907    // Wrapped over, fourth diagnostic
10908    cx.update_editor(|editor, window, cx| {
10909        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10910    });
10911    cx.assert_editor_state(indoc! {"
10912        fn func(abc def: i32) -> ˇu32 {
10913        }
10914    "});
10915
10916    cx.update_editor(|editor, window, cx| {
10917        editor.move_to_beginning(&MoveToBeginning, window, cx);
10918    });
10919    cx.assert_editor_state(indoc! {"
10920        ˇfn func(abc def: i32) -> u32 {
10921        }
10922    "});
10923
10924    //// Forward
10925
10926    // First diagnostic
10927    cx.update_editor(|editor, window, cx| {
10928        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10929    });
10930    cx.assert_editor_state(indoc! {"
10931        fn func(abcˇ def: i32) -> u32 {
10932        }
10933    "});
10934
10935    // Second diagnostic
10936    cx.update_editor(|editor, window, cx| {
10937        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10938    });
10939    cx.assert_editor_state(indoc! {"
10940        fn func(abc ˇdef: i32) -> u32 {
10941        }
10942    "});
10943
10944    // Third diagnostic, same place
10945    cx.update_editor(|editor, window, cx| {
10946        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10947    });
10948    cx.assert_editor_state(indoc! {"
10949        fn func(abc ˇdef: i32) -> u32 {
10950        }
10951    "});
10952
10953    // Fourth diagnostic
10954    cx.update_editor(|editor, window, cx| {
10955        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10956    });
10957    cx.assert_editor_state(indoc! {"
10958        fn func(abc def: i32) -> ˇu32 {
10959        }
10960    "});
10961
10962    // Wrapped around, first diagnostic
10963    cx.update_editor(|editor, window, cx| {
10964        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10965    });
10966    cx.assert_editor_state(indoc! {"
10967        fn func(abcˇ def: i32) -> u32 {
10968        }
10969    "});
10970}
10971
10972#[gpui::test]
10973async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10974    init_test(cx, |_| {});
10975
10976    let mut cx = EditorTestContext::new(cx).await;
10977
10978    cx.set_state(indoc! {"
10979        fn func(abˇc def: i32) -> u32 {
10980        }
10981    "});
10982    let lsp_store =
10983        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10984
10985    cx.update(|_, cx| {
10986        lsp_store.update(cx, |lsp_store, cx| {
10987            lsp_store.update_diagnostics(
10988                LanguageServerId(0),
10989                lsp::PublishDiagnosticsParams {
10990                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10991                    version: None,
10992                    diagnostics: vec![lsp::Diagnostic {
10993                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10994                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10995                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10996                        ..Default::default()
10997                    }],
10998                },
10999                &[],
11000                cx,
11001            )
11002        })
11003    }).unwrap();
11004    cx.run_until_parked();
11005    cx.update_editor(|editor, window, cx| {
11006        hover_popover::hover(editor, &Default::default(), window, cx)
11007    });
11008    cx.run_until_parked();
11009    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11010}
11011
11012#[gpui::test]
11013async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11014    init_test(cx, |_| {});
11015
11016    let mut cx = EditorTestContext::new(cx).await;
11017
11018    let diff_base = r#"
11019        use some::mod;
11020
11021        const A: u32 = 42;
11022
11023        fn main() {
11024            println!("hello");
11025
11026            println!("world");
11027        }
11028        "#
11029    .unindent();
11030
11031    // Edits are modified, removed, modified, added
11032    cx.set_state(
11033        &r#"
11034        use some::modified;
11035
11036        ˇ
11037        fn main() {
11038            println!("hello there");
11039
11040            println!("around the");
11041            println!("world");
11042        }
11043        "#
11044        .unindent(),
11045    );
11046
11047    cx.set_head_text(&diff_base);
11048    executor.run_until_parked();
11049
11050    cx.update_editor(|editor, window, cx| {
11051        //Wrap around the bottom of the buffer
11052        for _ in 0..3 {
11053            editor.go_to_next_hunk(&GoToHunk, window, cx);
11054        }
11055    });
11056
11057    cx.assert_editor_state(
11058        &r#"
11059        ˇuse some::modified;
11060
11061
11062        fn main() {
11063            println!("hello there");
11064
11065            println!("around the");
11066            println!("world");
11067        }
11068        "#
11069        .unindent(),
11070    );
11071
11072    cx.update_editor(|editor, window, cx| {
11073        //Wrap around the top of the buffer
11074        for _ in 0..2 {
11075            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11076        }
11077    });
11078
11079    cx.assert_editor_state(
11080        &r#"
11081        use some::modified;
11082
11083
11084        fn main() {
11085        ˇ    println!("hello there");
11086
11087            println!("around the");
11088            println!("world");
11089        }
11090        "#
11091        .unindent(),
11092    );
11093
11094    cx.update_editor(|editor, window, cx| {
11095        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11096    });
11097
11098    cx.assert_editor_state(
11099        &r#"
11100        use some::modified;
11101
11102        ˇ
11103        fn main() {
11104            println!("hello there");
11105
11106            println!("around the");
11107            println!("world");
11108        }
11109        "#
11110        .unindent(),
11111    );
11112
11113    cx.update_editor(|editor, window, cx| {
11114        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11115    });
11116
11117    cx.assert_editor_state(
11118        &r#"
11119        ˇuse some::modified;
11120
11121
11122        fn main() {
11123            println!("hello there");
11124
11125            println!("around the");
11126            println!("world");
11127        }
11128        "#
11129        .unindent(),
11130    );
11131
11132    cx.update_editor(|editor, window, cx| {
11133        for _ in 0..2 {
11134            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11135        }
11136    });
11137
11138    cx.assert_editor_state(
11139        &r#"
11140        use some::modified;
11141
11142
11143        fn main() {
11144        ˇ    println!("hello there");
11145
11146            println!("around the");
11147            println!("world");
11148        }
11149        "#
11150        .unindent(),
11151    );
11152
11153    cx.update_editor(|editor, window, cx| {
11154        editor.fold(&Fold, window, cx);
11155    });
11156
11157    cx.update_editor(|editor, window, cx| {
11158        editor.go_to_next_hunk(&GoToHunk, window, cx);
11159    });
11160
11161    cx.assert_editor_state(
11162        &r#"
11163        ˇuse some::modified;
11164
11165
11166        fn main() {
11167            println!("hello there");
11168
11169            println!("around the");
11170            println!("world");
11171        }
11172        "#
11173        .unindent(),
11174    );
11175}
11176
11177#[test]
11178fn test_split_words() {
11179    fn split(text: &str) -> Vec<&str> {
11180        split_words(text).collect()
11181    }
11182
11183    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11184    assert_eq!(split("hello_world"), &["hello_", "world"]);
11185    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11186    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11187    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11188    assert_eq!(split("helloworld"), &["helloworld"]);
11189
11190    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11191}
11192
11193#[gpui::test]
11194async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11195    init_test(cx, |_| {});
11196
11197    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11198    let mut assert = |before, after| {
11199        let _state_context = cx.set_state(before);
11200        cx.run_until_parked();
11201        cx.update_editor(|editor, window, cx| {
11202            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11203        });
11204        cx.assert_editor_state(after);
11205    };
11206
11207    // Outside bracket jumps to outside of matching bracket
11208    assert("console.logˇ(var);", "console.log(var)ˇ;");
11209    assert("console.log(var)ˇ;", "console.logˇ(var);");
11210
11211    // Inside bracket jumps to inside of matching bracket
11212    assert("console.log(ˇvar);", "console.log(varˇ);");
11213    assert("console.log(varˇ);", "console.log(ˇvar);");
11214
11215    // When outside a bracket and inside, favor jumping to the inside bracket
11216    assert(
11217        "console.log('foo', [1, 2, 3]ˇ);",
11218        "console.log(ˇ'foo', [1, 2, 3]);",
11219    );
11220    assert(
11221        "console.log(ˇ'foo', [1, 2, 3]);",
11222        "console.log('foo', [1, 2, 3]ˇ);",
11223    );
11224
11225    // Bias forward if two options are equally likely
11226    assert(
11227        "let result = curried_fun()ˇ();",
11228        "let result = curried_fun()()ˇ;",
11229    );
11230
11231    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11232    assert(
11233        indoc! {"
11234            function test() {
11235                console.log('test')ˇ
11236            }"},
11237        indoc! {"
11238            function test() {
11239                console.logˇ('test')
11240            }"},
11241    );
11242}
11243
11244#[gpui::test]
11245async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11246    init_test(cx, |_| {});
11247
11248    let fs = FakeFs::new(cx.executor());
11249    fs.insert_tree(
11250        path!("/a"),
11251        json!({
11252            "main.rs": "fn main() { let a = 5; }",
11253            "other.rs": "// Test file",
11254        }),
11255    )
11256    .await;
11257    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11258
11259    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11260    language_registry.add(Arc::new(Language::new(
11261        LanguageConfig {
11262            name: "Rust".into(),
11263            matcher: LanguageMatcher {
11264                path_suffixes: vec!["rs".to_string()],
11265                ..Default::default()
11266            },
11267            brackets: BracketPairConfig {
11268                pairs: vec![BracketPair {
11269                    start: "{".to_string(),
11270                    end: "}".to_string(),
11271                    close: true,
11272                    surround: true,
11273                    newline: true,
11274                }],
11275                disabled_scopes_by_bracket_ix: Vec::new(),
11276            },
11277            ..Default::default()
11278        },
11279        Some(tree_sitter_rust::LANGUAGE.into()),
11280    )));
11281    let mut fake_servers = language_registry.register_fake_lsp(
11282        "Rust",
11283        FakeLspAdapter {
11284            capabilities: lsp::ServerCapabilities {
11285                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11286                    first_trigger_character: "{".to_string(),
11287                    more_trigger_character: None,
11288                }),
11289                ..Default::default()
11290            },
11291            ..Default::default()
11292        },
11293    );
11294
11295    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11296
11297    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11298
11299    let worktree_id = workspace
11300        .update(cx, |workspace, _, cx| {
11301            workspace.project().update(cx, |project, cx| {
11302                project.worktrees(cx).next().unwrap().read(cx).id()
11303            })
11304        })
11305        .unwrap();
11306
11307    let buffer = project
11308        .update(cx, |project, cx| {
11309            project.open_local_buffer(path!("/a/main.rs"), cx)
11310        })
11311        .await
11312        .unwrap();
11313    let editor_handle = workspace
11314        .update(cx, |workspace, window, cx| {
11315            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11316        })
11317        .unwrap()
11318        .await
11319        .unwrap()
11320        .downcast::<Editor>()
11321        .unwrap();
11322
11323    cx.executor().start_waiting();
11324    let fake_server = fake_servers.next().await.unwrap();
11325
11326    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11327        assert_eq!(
11328            params.text_document_position.text_document.uri,
11329            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11330        );
11331        assert_eq!(
11332            params.text_document_position.position,
11333            lsp::Position::new(0, 21),
11334        );
11335
11336        Ok(Some(vec![lsp::TextEdit {
11337            new_text: "]".to_string(),
11338            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11339        }]))
11340    });
11341
11342    editor_handle.update_in(cx, |editor, window, cx| {
11343        window.focus(&editor.focus_handle(cx));
11344        editor.change_selections(None, window, cx, |s| {
11345            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11346        });
11347        editor.handle_input("{", window, cx);
11348    });
11349
11350    cx.executor().run_until_parked();
11351
11352    buffer.update(cx, |buffer, _| {
11353        assert_eq!(
11354            buffer.text(),
11355            "fn main() { let a = {5}; }",
11356            "No extra braces from on type formatting should appear in the buffer"
11357        )
11358    });
11359}
11360
11361#[gpui::test]
11362async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11363    init_test(cx, |_| {});
11364
11365    let fs = FakeFs::new(cx.executor());
11366    fs.insert_tree(
11367        path!("/a"),
11368        json!({
11369            "main.rs": "fn main() { let a = 5; }",
11370            "other.rs": "// Test file",
11371        }),
11372    )
11373    .await;
11374
11375    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11376
11377    let server_restarts = Arc::new(AtomicUsize::new(0));
11378    let closure_restarts = Arc::clone(&server_restarts);
11379    let language_server_name = "test language server";
11380    let language_name: LanguageName = "Rust".into();
11381
11382    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11383    language_registry.add(Arc::new(Language::new(
11384        LanguageConfig {
11385            name: language_name.clone(),
11386            matcher: LanguageMatcher {
11387                path_suffixes: vec!["rs".to_string()],
11388                ..Default::default()
11389            },
11390            ..Default::default()
11391        },
11392        Some(tree_sitter_rust::LANGUAGE.into()),
11393    )));
11394    let mut fake_servers = language_registry.register_fake_lsp(
11395        "Rust",
11396        FakeLspAdapter {
11397            name: language_server_name,
11398            initialization_options: Some(json!({
11399                "testOptionValue": true
11400            })),
11401            initializer: Some(Box::new(move |fake_server| {
11402                let task_restarts = Arc::clone(&closure_restarts);
11403                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11404                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11405                    futures::future::ready(Ok(()))
11406                });
11407            })),
11408            ..Default::default()
11409        },
11410    );
11411
11412    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11413    let _buffer = project
11414        .update(cx, |project, cx| {
11415            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11416        })
11417        .await
11418        .unwrap();
11419    let _fake_server = fake_servers.next().await.unwrap();
11420    update_test_language_settings(cx, |language_settings| {
11421        language_settings.languages.insert(
11422            language_name.clone(),
11423            LanguageSettingsContent {
11424                tab_size: NonZeroU32::new(8),
11425                ..Default::default()
11426            },
11427        );
11428    });
11429    cx.executor().run_until_parked();
11430    assert_eq!(
11431        server_restarts.load(atomic::Ordering::Acquire),
11432        0,
11433        "Should not restart LSP server on an unrelated change"
11434    );
11435
11436    update_test_project_settings(cx, |project_settings| {
11437        project_settings.lsp.insert(
11438            "Some other server name".into(),
11439            LspSettings {
11440                binary: None,
11441                settings: None,
11442                initialization_options: Some(json!({
11443                    "some other init value": false
11444                })),
11445            },
11446        );
11447    });
11448    cx.executor().run_until_parked();
11449    assert_eq!(
11450        server_restarts.load(atomic::Ordering::Acquire),
11451        0,
11452        "Should not restart LSP server on an unrelated LSP settings change"
11453    );
11454
11455    update_test_project_settings(cx, |project_settings| {
11456        project_settings.lsp.insert(
11457            language_server_name.into(),
11458            LspSettings {
11459                binary: None,
11460                settings: None,
11461                initialization_options: Some(json!({
11462                    "anotherInitValue": false
11463                })),
11464            },
11465        );
11466    });
11467    cx.executor().run_until_parked();
11468    assert_eq!(
11469        server_restarts.load(atomic::Ordering::Acquire),
11470        1,
11471        "Should restart LSP server on a related LSP settings change"
11472    );
11473
11474    update_test_project_settings(cx, |project_settings| {
11475        project_settings.lsp.insert(
11476            language_server_name.into(),
11477            LspSettings {
11478                binary: None,
11479                settings: None,
11480                initialization_options: Some(json!({
11481                    "anotherInitValue": false
11482                })),
11483            },
11484        );
11485    });
11486    cx.executor().run_until_parked();
11487    assert_eq!(
11488        server_restarts.load(atomic::Ordering::Acquire),
11489        1,
11490        "Should not restart LSP server on a related LSP settings change that is the same"
11491    );
11492
11493    update_test_project_settings(cx, |project_settings| {
11494        project_settings.lsp.insert(
11495            language_server_name.into(),
11496            LspSettings {
11497                binary: None,
11498                settings: None,
11499                initialization_options: None,
11500            },
11501        );
11502    });
11503    cx.executor().run_until_parked();
11504    assert_eq!(
11505        server_restarts.load(atomic::Ordering::Acquire),
11506        2,
11507        "Should restart LSP server on another related LSP settings change"
11508    );
11509}
11510
11511#[gpui::test]
11512async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11513    init_test(cx, |_| {});
11514
11515    let mut cx = EditorLspTestContext::new_rust(
11516        lsp::ServerCapabilities {
11517            completion_provider: Some(lsp::CompletionOptions {
11518                trigger_characters: Some(vec![".".to_string()]),
11519                resolve_provider: Some(true),
11520                ..Default::default()
11521            }),
11522            ..Default::default()
11523        },
11524        cx,
11525    )
11526    .await;
11527
11528    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11529    cx.simulate_keystroke(".");
11530    let completion_item = lsp::CompletionItem {
11531        label: "some".into(),
11532        kind: Some(lsp::CompletionItemKind::SNIPPET),
11533        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11534        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11535            kind: lsp::MarkupKind::Markdown,
11536            value: "```rust\nSome(2)\n```".to_string(),
11537        })),
11538        deprecated: Some(false),
11539        sort_text: Some("fffffff2".to_string()),
11540        filter_text: Some("some".to_string()),
11541        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11542        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11543            range: lsp::Range {
11544                start: lsp::Position {
11545                    line: 0,
11546                    character: 22,
11547                },
11548                end: lsp::Position {
11549                    line: 0,
11550                    character: 22,
11551                },
11552            },
11553            new_text: "Some(2)".to_string(),
11554        })),
11555        additional_text_edits: Some(vec![lsp::TextEdit {
11556            range: lsp::Range {
11557                start: lsp::Position {
11558                    line: 0,
11559                    character: 20,
11560                },
11561                end: lsp::Position {
11562                    line: 0,
11563                    character: 22,
11564                },
11565            },
11566            new_text: "".to_string(),
11567        }]),
11568        ..Default::default()
11569    };
11570
11571    let closure_completion_item = completion_item.clone();
11572    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11573        let task_completion_item = closure_completion_item.clone();
11574        async move {
11575            Ok(Some(lsp::CompletionResponse::Array(vec![
11576                task_completion_item,
11577            ])))
11578        }
11579    });
11580
11581    request.next().await;
11582
11583    cx.condition(|editor, _| editor.context_menu_visible())
11584        .await;
11585    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11586        editor
11587            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11588            .unwrap()
11589    });
11590    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11591
11592    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11593        let task_completion_item = completion_item.clone();
11594        async move { Ok(task_completion_item) }
11595    })
11596    .next()
11597    .await
11598    .unwrap();
11599    apply_additional_edits.await.unwrap();
11600    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11601}
11602
11603#[gpui::test]
11604async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11605    init_test(cx, |_| {});
11606
11607    let mut cx = EditorLspTestContext::new_rust(
11608        lsp::ServerCapabilities {
11609            completion_provider: Some(lsp::CompletionOptions {
11610                trigger_characters: Some(vec![".".to_string()]),
11611                resolve_provider: Some(true),
11612                ..Default::default()
11613            }),
11614            ..Default::default()
11615        },
11616        cx,
11617    )
11618    .await;
11619
11620    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11621    cx.simulate_keystroke(".");
11622
11623    let item1 = lsp::CompletionItem {
11624        label: "method id()".to_string(),
11625        filter_text: Some("id".to_string()),
11626        detail: None,
11627        documentation: None,
11628        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11629            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11630            new_text: ".id".to_string(),
11631        })),
11632        ..lsp::CompletionItem::default()
11633    };
11634
11635    let item2 = lsp::CompletionItem {
11636        label: "other".to_string(),
11637        filter_text: Some("other".to_string()),
11638        detail: None,
11639        documentation: None,
11640        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11641            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11642            new_text: ".other".to_string(),
11643        })),
11644        ..lsp::CompletionItem::default()
11645    };
11646
11647    let item1 = item1.clone();
11648    cx.handle_request::<lsp::request::Completion, _, _>({
11649        let item1 = item1.clone();
11650        move |_, _, _| {
11651            let item1 = item1.clone();
11652            let item2 = item2.clone();
11653            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11654        }
11655    })
11656    .next()
11657    .await;
11658
11659    cx.condition(|editor, _| editor.context_menu_visible())
11660        .await;
11661    cx.update_editor(|editor, _, _| {
11662        let context_menu = editor.context_menu.borrow_mut();
11663        let context_menu = context_menu
11664            .as_ref()
11665            .expect("Should have the context menu deployed");
11666        match context_menu {
11667            CodeContextMenu::Completions(completions_menu) => {
11668                let completions = completions_menu.completions.borrow_mut();
11669                assert_eq!(
11670                    completions
11671                        .iter()
11672                        .map(|completion| &completion.label.text)
11673                        .collect::<Vec<_>>(),
11674                    vec!["method id()", "other"]
11675                )
11676            }
11677            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11678        }
11679    });
11680
11681    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11682        let item1 = item1.clone();
11683        move |_, item_to_resolve, _| {
11684            let item1 = item1.clone();
11685            async move {
11686                if item1 == item_to_resolve {
11687                    Ok(lsp::CompletionItem {
11688                        label: "method id()".to_string(),
11689                        filter_text: Some("id".to_string()),
11690                        detail: Some("Now resolved!".to_string()),
11691                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11692                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11693                            range: lsp::Range::new(
11694                                lsp::Position::new(0, 22),
11695                                lsp::Position::new(0, 22),
11696                            ),
11697                            new_text: ".id".to_string(),
11698                        })),
11699                        ..lsp::CompletionItem::default()
11700                    })
11701                } else {
11702                    Ok(item_to_resolve)
11703                }
11704            }
11705        }
11706    })
11707    .next()
11708    .await
11709    .unwrap();
11710    cx.run_until_parked();
11711
11712    cx.update_editor(|editor, window, cx| {
11713        editor.context_menu_next(&Default::default(), window, cx);
11714    });
11715
11716    cx.update_editor(|editor, _, _| {
11717        let context_menu = editor.context_menu.borrow_mut();
11718        let context_menu = context_menu
11719            .as_ref()
11720            .expect("Should have the context menu deployed");
11721        match context_menu {
11722            CodeContextMenu::Completions(completions_menu) => {
11723                let completions = completions_menu.completions.borrow_mut();
11724                assert_eq!(
11725                    completions
11726                        .iter()
11727                        .map(|completion| &completion.label.text)
11728                        .collect::<Vec<_>>(),
11729                    vec!["method id() Now resolved!", "other"],
11730                    "Should update first completion label, but not second as the filter text did not match."
11731                );
11732            }
11733            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11734        }
11735    });
11736}
11737
11738#[gpui::test]
11739async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
11740    init_test(cx, |_| {});
11741
11742    let mut cx = EditorLspTestContext::new_rust(
11743        lsp::ServerCapabilities {
11744            completion_provider: Some(lsp::CompletionOptions {
11745                trigger_characters: Some(vec![".".to_string()]),
11746                resolve_provider: Some(true),
11747                ..Default::default()
11748            }),
11749            ..Default::default()
11750        },
11751        cx,
11752    )
11753    .await;
11754
11755    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11756    cx.simulate_keystroke(".");
11757
11758    let unresolved_item_1 = lsp::CompletionItem {
11759        label: "id".to_string(),
11760        filter_text: Some("id".to_string()),
11761        detail: None,
11762        documentation: None,
11763        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11764            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11765            new_text: ".id".to_string(),
11766        })),
11767        ..lsp::CompletionItem::default()
11768    };
11769    let resolved_item_1 = lsp::CompletionItem {
11770        additional_text_edits: Some(vec![lsp::TextEdit {
11771            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11772            new_text: "!!".to_string(),
11773        }]),
11774        ..unresolved_item_1.clone()
11775    };
11776    let unresolved_item_2 = lsp::CompletionItem {
11777        label: "other".to_string(),
11778        filter_text: Some("other".to_string()),
11779        detail: None,
11780        documentation: None,
11781        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11782            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11783            new_text: ".other".to_string(),
11784        })),
11785        ..lsp::CompletionItem::default()
11786    };
11787    let resolved_item_2 = lsp::CompletionItem {
11788        additional_text_edits: Some(vec![lsp::TextEdit {
11789            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11790            new_text: "??".to_string(),
11791        }]),
11792        ..unresolved_item_2.clone()
11793    };
11794
11795    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11796    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11797    cx.lsp
11798        .server
11799        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11800            let unresolved_item_1 = unresolved_item_1.clone();
11801            let resolved_item_1 = resolved_item_1.clone();
11802            let unresolved_item_2 = unresolved_item_2.clone();
11803            let resolved_item_2 = resolved_item_2.clone();
11804            let resolve_requests_1 = resolve_requests_1.clone();
11805            let resolve_requests_2 = resolve_requests_2.clone();
11806            move |unresolved_request, _| {
11807                let unresolved_item_1 = unresolved_item_1.clone();
11808                let resolved_item_1 = resolved_item_1.clone();
11809                let unresolved_item_2 = unresolved_item_2.clone();
11810                let resolved_item_2 = resolved_item_2.clone();
11811                let resolve_requests_1 = resolve_requests_1.clone();
11812                let resolve_requests_2 = resolve_requests_2.clone();
11813                async move {
11814                    if unresolved_request == unresolved_item_1 {
11815                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11816                        Ok(resolved_item_1.clone())
11817                    } else if unresolved_request == unresolved_item_2 {
11818                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11819                        Ok(resolved_item_2.clone())
11820                    } else {
11821                        panic!("Unexpected completion item {unresolved_request:?}")
11822                    }
11823                }
11824            }
11825        })
11826        .detach();
11827
11828    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11829        let unresolved_item_1 = unresolved_item_1.clone();
11830        let unresolved_item_2 = unresolved_item_2.clone();
11831        async move {
11832            Ok(Some(lsp::CompletionResponse::Array(vec![
11833                unresolved_item_1,
11834                unresolved_item_2,
11835            ])))
11836        }
11837    })
11838    .next()
11839    .await;
11840
11841    cx.condition(|editor, _| editor.context_menu_visible())
11842        .await;
11843    cx.update_editor(|editor, _, _| {
11844        let context_menu = editor.context_menu.borrow_mut();
11845        let context_menu = context_menu
11846            .as_ref()
11847            .expect("Should have the context menu deployed");
11848        match context_menu {
11849            CodeContextMenu::Completions(completions_menu) => {
11850                let completions = completions_menu.completions.borrow_mut();
11851                assert_eq!(
11852                    completions
11853                        .iter()
11854                        .map(|completion| &completion.label.text)
11855                        .collect::<Vec<_>>(),
11856                    vec!["id", "other"]
11857                )
11858            }
11859            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11860        }
11861    });
11862    cx.run_until_parked();
11863
11864    cx.update_editor(|editor, window, cx| {
11865        editor.context_menu_next(&ContextMenuNext, window, cx);
11866    });
11867    cx.run_until_parked();
11868    cx.update_editor(|editor, window, cx| {
11869        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11870    });
11871    cx.run_until_parked();
11872    cx.update_editor(|editor, window, cx| {
11873        editor.context_menu_next(&ContextMenuNext, window, cx);
11874    });
11875    cx.run_until_parked();
11876    cx.update_editor(|editor, window, cx| {
11877        editor
11878            .compose_completion(&ComposeCompletion::default(), window, cx)
11879            .expect("No task returned")
11880    })
11881    .await
11882    .expect("Completion failed");
11883    cx.run_until_parked();
11884
11885    cx.update_editor(|editor, _, cx| {
11886        assert_eq!(
11887            resolve_requests_1.load(atomic::Ordering::Acquire),
11888            1,
11889            "Should always resolve once despite multiple selections"
11890        );
11891        assert_eq!(
11892            resolve_requests_2.load(atomic::Ordering::Acquire),
11893            1,
11894            "Should always resolve once after multiple selections and applying the completion"
11895        );
11896        assert_eq!(
11897            editor.text(cx),
11898            "fn main() { let a = ??.other; }",
11899            "Should use resolved data when applying the completion"
11900        );
11901    });
11902}
11903
11904#[gpui::test]
11905async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
11906    init_test(cx, |_| {});
11907
11908    let item_0 = lsp::CompletionItem {
11909        label: "abs".into(),
11910        insert_text: Some("abs".into()),
11911        data: Some(json!({ "very": "special"})),
11912        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11913        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11914            lsp::InsertReplaceEdit {
11915                new_text: "abs".to_string(),
11916                insert: lsp::Range::default(),
11917                replace: lsp::Range::default(),
11918            },
11919        )),
11920        ..lsp::CompletionItem::default()
11921    };
11922    let items = iter::once(item_0.clone())
11923        .chain((11..51).map(|i| lsp::CompletionItem {
11924            label: format!("item_{}", i),
11925            insert_text: Some(format!("item_{}", i)),
11926            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11927            ..lsp::CompletionItem::default()
11928        }))
11929        .collect::<Vec<_>>();
11930
11931    let default_commit_characters = vec!["?".to_string()];
11932    let default_data = json!({ "default": "data"});
11933    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11934    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11935    let default_edit_range = lsp::Range {
11936        start: lsp::Position {
11937            line: 0,
11938            character: 5,
11939        },
11940        end: lsp::Position {
11941            line: 0,
11942            character: 5,
11943        },
11944    };
11945
11946    let item_0_out = lsp::CompletionItem {
11947        commit_characters: Some(default_commit_characters.clone()),
11948        insert_text_format: Some(default_insert_text_format),
11949        ..item_0
11950    };
11951    let items_out = iter::once(item_0_out)
11952        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11953            commit_characters: Some(default_commit_characters.clone()),
11954            data: Some(default_data.clone()),
11955            insert_text_mode: Some(default_insert_text_mode),
11956            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11957                range: default_edit_range,
11958                new_text: item.label.clone(),
11959            })),
11960            ..item.clone()
11961        }))
11962        .collect::<Vec<lsp::CompletionItem>>();
11963
11964    let mut cx = EditorLspTestContext::new_rust(
11965        lsp::ServerCapabilities {
11966            completion_provider: Some(lsp::CompletionOptions {
11967                trigger_characters: Some(vec![".".to_string()]),
11968                resolve_provider: Some(true),
11969                ..Default::default()
11970            }),
11971            ..Default::default()
11972        },
11973        cx,
11974    )
11975    .await;
11976
11977    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11978    cx.simulate_keystroke(".");
11979
11980    let completion_data = default_data.clone();
11981    let completion_characters = default_commit_characters.clone();
11982    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11983        let default_data = completion_data.clone();
11984        let default_commit_characters = completion_characters.clone();
11985        let items = items.clone();
11986        async move {
11987            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11988                items,
11989                item_defaults: Some(lsp::CompletionListItemDefaults {
11990                    data: Some(default_data.clone()),
11991                    commit_characters: Some(default_commit_characters.clone()),
11992                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11993                        default_edit_range,
11994                    )),
11995                    insert_text_format: Some(default_insert_text_format),
11996                    insert_text_mode: Some(default_insert_text_mode),
11997                }),
11998                ..lsp::CompletionList::default()
11999            })))
12000        }
12001    })
12002    .next()
12003    .await;
12004
12005    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12006    cx.lsp
12007        .server
12008        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12009            let closure_resolved_items = resolved_items.clone();
12010            move |item_to_resolve, _| {
12011                let closure_resolved_items = closure_resolved_items.clone();
12012                async move {
12013                    closure_resolved_items.lock().push(item_to_resolve.clone());
12014                    Ok(item_to_resolve)
12015                }
12016            }
12017        })
12018        .detach();
12019
12020    cx.condition(|editor, _| editor.context_menu_visible())
12021        .await;
12022    cx.run_until_parked();
12023    cx.update_editor(|editor, _, _| {
12024        let menu = editor.context_menu.borrow_mut();
12025        match menu.as_ref().expect("should have the completions menu") {
12026            CodeContextMenu::Completions(completions_menu) => {
12027                assert_eq!(
12028                    completions_menu
12029                        .entries
12030                        .borrow()
12031                        .iter()
12032                        .map(|mat| mat.string.clone())
12033                        .collect::<Vec<String>>(),
12034                    items_out
12035                        .iter()
12036                        .map(|completion| completion.label.clone())
12037                        .collect::<Vec<String>>()
12038                );
12039            }
12040            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12041        }
12042    });
12043    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12044    // with 4 from the end.
12045    assert_eq!(
12046        *resolved_items.lock(),
12047        [
12048            &items_out[0..16],
12049            &items_out[items_out.len() - 4..items_out.len()]
12050        ]
12051        .concat()
12052        .iter()
12053        .cloned()
12054        .collect::<Vec<lsp::CompletionItem>>()
12055    );
12056    resolved_items.lock().clear();
12057
12058    cx.update_editor(|editor, window, cx| {
12059        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12060    });
12061    cx.run_until_parked();
12062    // Completions that have already been resolved are skipped.
12063    assert_eq!(
12064        *resolved_items.lock(),
12065        items_out[items_out.len() - 16..items_out.len() - 4]
12066            .iter()
12067            .cloned()
12068            .collect::<Vec<lsp::CompletionItem>>()
12069    );
12070    resolved_items.lock().clear();
12071}
12072
12073#[gpui::test]
12074async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12075    init_test(cx, |_| {});
12076
12077    let mut cx = EditorLspTestContext::new(
12078        Language::new(
12079            LanguageConfig {
12080                matcher: LanguageMatcher {
12081                    path_suffixes: vec!["jsx".into()],
12082                    ..Default::default()
12083                },
12084                overrides: [(
12085                    "element".into(),
12086                    LanguageConfigOverride {
12087                        word_characters: Override::Set(['-'].into_iter().collect()),
12088                        ..Default::default()
12089                    },
12090                )]
12091                .into_iter()
12092                .collect(),
12093                ..Default::default()
12094            },
12095            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12096        )
12097        .with_override_query("(jsx_self_closing_element) @element")
12098        .unwrap(),
12099        lsp::ServerCapabilities {
12100            completion_provider: Some(lsp::CompletionOptions {
12101                trigger_characters: Some(vec![":".to_string()]),
12102                ..Default::default()
12103            }),
12104            ..Default::default()
12105        },
12106        cx,
12107    )
12108    .await;
12109
12110    cx.lsp
12111        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12112            Ok(Some(lsp::CompletionResponse::Array(vec![
12113                lsp::CompletionItem {
12114                    label: "bg-blue".into(),
12115                    ..Default::default()
12116                },
12117                lsp::CompletionItem {
12118                    label: "bg-red".into(),
12119                    ..Default::default()
12120                },
12121                lsp::CompletionItem {
12122                    label: "bg-yellow".into(),
12123                    ..Default::default()
12124                },
12125            ])))
12126        });
12127
12128    cx.set_state(r#"<p class="bgˇ" />"#);
12129
12130    // Trigger completion when typing a dash, because the dash is an extra
12131    // word character in the 'element' scope, which contains the cursor.
12132    cx.simulate_keystroke("-");
12133    cx.executor().run_until_parked();
12134    cx.update_editor(|editor, _, _| {
12135        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12136        {
12137            assert_eq!(
12138                completion_menu_entries(&menu),
12139                &["bg-red", "bg-blue", "bg-yellow"]
12140            );
12141        } else {
12142            panic!("expected completion menu to be open");
12143        }
12144    });
12145
12146    cx.simulate_keystroke("l");
12147    cx.executor().run_until_parked();
12148    cx.update_editor(|editor, _, _| {
12149        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12150        {
12151            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12152        } else {
12153            panic!("expected completion menu to be open");
12154        }
12155    });
12156
12157    // When filtering completions, consider the character after the '-' to
12158    // be the start of a subword.
12159    cx.set_state(r#"<p class="yelˇ" />"#);
12160    cx.simulate_keystroke("l");
12161    cx.executor().run_until_parked();
12162    cx.update_editor(|editor, _, _| {
12163        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12164        {
12165            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12166        } else {
12167            panic!("expected completion menu to be open");
12168        }
12169    });
12170}
12171
12172fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12173    let entries = menu.entries.borrow();
12174    entries.iter().map(|mat| mat.string.clone()).collect()
12175}
12176
12177#[gpui::test]
12178async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12179    init_test(cx, |settings| {
12180        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12181            FormatterList(vec![Formatter::Prettier].into()),
12182        ))
12183    });
12184
12185    let fs = FakeFs::new(cx.executor());
12186    fs.insert_file(path!("/file.ts"), Default::default()).await;
12187
12188    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12189    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12190
12191    language_registry.add(Arc::new(Language::new(
12192        LanguageConfig {
12193            name: "TypeScript".into(),
12194            matcher: LanguageMatcher {
12195                path_suffixes: vec!["ts".to_string()],
12196                ..Default::default()
12197            },
12198            ..Default::default()
12199        },
12200        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12201    )));
12202    update_test_language_settings(cx, |settings| {
12203        settings.defaults.prettier = Some(PrettierSettings {
12204            allowed: true,
12205            ..PrettierSettings::default()
12206        });
12207    });
12208
12209    let test_plugin = "test_plugin";
12210    let _ = language_registry.register_fake_lsp(
12211        "TypeScript",
12212        FakeLspAdapter {
12213            prettier_plugins: vec![test_plugin],
12214            ..Default::default()
12215        },
12216    );
12217
12218    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12219    let buffer = project
12220        .update(cx, |project, cx| {
12221            project.open_local_buffer(path!("/file.ts"), cx)
12222        })
12223        .await
12224        .unwrap();
12225
12226    let buffer_text = "one\ntwo\nthree\n";
12227    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12228    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12229    editor.update_in(cx, |editor, window, cx| {
12230        editor.set_text(buffer_text, window, cx)
12231    });
12232
12233    editor
12234        .update_in(cx, |editor, window, cx| {
12235            editor.perform_format(
12236                project.clone(),
12237                FormatTrigger::Manual,
12238                FormatTarget::Buffers,
12239                window,
12240                cx,
12241            )
12242        })
12243        .unwrap()
12244        .await;
12245    assert_eq!(
12246        editor.update(cx, |editor, cx| editor.text(cx)),
12247        buffer_text.to_string() + prettier_format_suffix,
12248        "Test prettier formatting was not applied to the original buffer text",
12249    );
12250
12251    update_test_language_settings(cx, |settings| {
12252        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12253    });
12254    let format = editor.update_in(cx, |editor, window, cx| {
12255        editor.perform_format(
12256            project.clone(),
12257            FormatTrigger::Manual,
12258            FormatTarget::Buffers,
12259            window,
12260            cx,
12261        )
12262    });
12263    format.await.unwrap();
12264    assert_eq!(
12265        editor.update(cx, |editor, cx| editor.text(cx)),
12266        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12267        "Autoformatting (via test prettier) was not applied to the original buffer text",
12268    );
12269}
12270
12271#[gpui::test]
12272async fn test_addition_reverts(cx: &mut TestAppContext) {
12273    init_test(cx, |_| {});
12274    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12275    let base_text = indoc! {r#"
12276        struct Row;
12277        struct Row1;
12278        struct Row2;
12279
12280        struct Row4;
12281        struct Row5;
12282        struct Row6;
12283
12284        struct Row8;
12285        struct Row9;
12286        struct Row10;"#};
12287
12288    // When addition hunks are not adjacent to carets, no hunk revert is performed
12289    assert_hunk_revert(
12290        indoc! {r#"struct Row;
12291                   struct Row1;
12292                   struct Row1.1;
12293                   struct Row1.2;
12294                   struct Row2;ˇ
12295
12296                   struct Row4;
12297                   struct Row5;
12298                   struct Row6;
12299
12300                   struct Row8;
12301                   ˇstruct Row9;
12302                   struct Row9.1;
12303                   struct Row9.2;
12304                   struct Row9.3;
12305                   struct Row10;"#},
12306        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12307        indoc! {r#"struct Row;
12308                   struct Row1;
12309                   struct Row1.1;
12310                   struct Row1.2;
12311                   struct Row2;ˇ
12312
12313                   struct Row4;
12314                   struct Row5;
12315                   struct Row6;
12316
12317                   struct Row8;
12318                   ˇstruct Row9;
12319                   struct Row9.1;
12320                   struct Row9.2;
12321                   struct Row9.3;
12322                   struct Row10;"#},
12323        base_text,
12324        &mut cx,
12325    );
12326    // Same for selections
12327    assert_hunk_revert(
12328        indoc! {r#"struct Row;
12329                   struct Row1;
12330                   struct Row2;
12331                   struct Row2.1;
12332                   struct Row2.2;
12333                   «ˇ
12334                   struct Row4;
12335                   struct» Row5;
12336                   «struct Row6;
12337                   ˇ»
12338                   struct Row9.1;
12339                   struct Row9.2;
12340                   struct Row9.3;
12341                   struct Row8;
12342                   struct Row9;
12343                   struct Row10;"#},
12344        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12345        indoc! {r#"struct Row;
12346                   struct Row1;
12347                   struct Row2;
12348                   struct Row2.1;
12349                   struct Row2.2;
12350                   «ˇ
12351                   struct Row4;
12352                   struct» Row5;
12353                   «struct Row6;
12354                   ˇ»
12355                   struct Row9.1;
12356                   struct Row9.2;
12357                   struct Row9.3;
12358                   struct Row8;
12359                   struct Row9;
12360                   struct Row10;"#},
12361        base_text,
12362        &mut cx,
12363    );
12364
12365    // When carets and selections intersect the addition hunks, those are reverted.
12366    // Adjacent carets got merged.
12367    assert_hunk_revert(
12368        indoc! {r#"struct Row;
12369                   ˇ// something on the top
12370                   struct Row1;
12371                   struct Row2;
12372                   struct Roˇw3.1;
12373                   struct Row2.2;
12374                   struct Row2.3;ˇ
12375
12376                   struct Row4;
12377                   struct ˇRow5.1;
12378                   struct Row5.2;
12379                   struct «Rowˇ»5.3;
12380                   struct Row5;
12381                   struct Row6;
12382                   ˇ
12383                   struct Row9.1;
12384                   struct «Rowˇ»9.2;
12385                   struct «ˇRow»9.3;
12386                   struct Row8;
12387                   struct Row9;
12388                   «ˇ// something on bottom»
12389                   struct Row10;"#},
12390        vec![
12391            DiffHunkStatus::added_none(),
12392            DiffHunkStatus::added_none(),
12393            DiffHunkStatus::added_none(),
12394            DiffHunkStatus::added_none(),
12395            DiffHunkStatus::added_none(),
12396        ],
12397        indoc! {r#"struct Row;
12398                   ˇstruct Row1;
12399                   struct Row2;
12400                   ˇ
12401                   struct Row4;
12402                   ˇstruct Row5;
12403                   struct Row6;
12404                   ˇ
12405                   ˇstruct Row8;
12406                   struct Row9;
12407                   ˇstruct Row10;"#},
12408        base_text,
12409        &mut cx,
12410    );
12411}
12412
12413#[gpui::test]
12414async fn test_modification_reverts(cx: &mut TestAppContext) {
12415    init_test(cx, |_| {});
12416    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12417    let base_text = indoc! {r#"
12418        struct Row;
12419        struct Row1;
12420        struct Row2;
12421
12422        struct Row4;
12423        struct Row5;
12424        struct Row6;
12425
12426        struct Row8;
12427        struct Row9;
12428        struct Row10;"#};
12429
12430    // Modification hunks behave the same as the addition ones.
12431    assert_hunk_revert(
12432        indoc! {r#"struct Row;
12433                   struct Row1;
12434                   struct Row33;
12435                   ˇ
12436                   struct Row4;
12437                   struct Row5;
12438                   struct Row6;
12439                   ˇ
12440                   struct Row99;
12441                   struct Row9;
12442                   struct Row10;"#},
12443        vec![
12444            DiffHunkStatus::modified_none(),
12445            DiffHunkStatus::modified_none(),
12446        ],
12447        indoc! {r#"struct Row;
12448                   struct Row1;
12449                   struct Row33;
12450                   ˇ
12451                   struct Row4;
12452                   struct Row5;
12453                   struct Row6;
12454                   ˇ
12455                   struct Row99;
12456                   struct Row9;
12457                   struct Row10;"#},
12458        base_text,
12459        &mut cx,
12460    );
12461    assert_hunk_revert(
12462        indoc! {r#"struct Row;
12463                   struct Row1;
12464                   struct Row33;
12465                   «ˇ
12466                   struct Row4;
12467                   struct» Row5;
12468                   «struct Row6;
12469                   ˇ»
12470                   struct Row99;
12471                   struct Row9;
12472                   struct Row10;"#},
12473        vec![
12474            DiffHunkStatus::modified_none(),
12475            DiffHunkStatus::modified_none(),
12476        ],
12477        indoc! {r#"struct Row;
12478                   struct Row1;
12479                   struct Row33;
12480                   «ˇ
12481                   struct Row4;
12482                   struct» Row5;
12483                   «struct Row6;
12484                   ˇ»
12485                   struct Row99;
12486                   struct Row9;
12487                   struct Row10;"#},
12488        base_text,
12489        &mut cx,
12490    );
12491
12492    assert_hunk_revert(
12493        indoc! {r#"ˇstruct Row1.1;
12494                   struct Row1;
12495                   «ˇstr»uct Row22;
12496
12497                   struct ˇRow44;
12498                   struct Row5;
12499                   struct «Rˇ»ow66;ˇ
12500
12501                   «struˇ»ct Row88;
12502                   struct Row9;
12503                   struct Row1011;ˇ"#},
12504        vec![
12505            DiffHunkStatus::modified_none(),
12506            DiffHunkStatus::modified_none(),
12507            DiffHunkStatus::modified_none(),
12508            DiffHunkStatus::modified_none(),
12509            DiffHunkStatus::modified_none(),
12510            DiffHunkStatus::modified_none(),
12511        ],
12512        indoc! {r#"struct Row;
12513                   ˇstruct Row1;
12514                   struct Row2;
12515                   ˇ
12516                   struct Row4;
12517                   ˇstruct Row5;
12518                   struct Row6;
12519                   ˇ
12520                   struct Row8;
12521                   ˇstruct Row9;
12522                   struct Row10;ˇ"#},
12523        base_text,
12524        &mut cx,
12525    );
12526}
12527
12528#[gpui::test]
12529async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12530    init_test(cx, |_| {});
12531    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12532    let base_text = indoc! {r#"
12533        one
12534
12535        two
12536        three
12537        "#};
12538
12539    cx.set_head_text(base_text);
12540    cx.set_state("\nˇ\n");
12541    cx.executor().run_until_parked();
12542    cx.update_editor(|editor, _window, cx| {
12543        editor.expand_selected_diff_hunks(cx);
12544    });
12545    cx.executor().run_until_parked();
12546    cx.update_editor(|editor, window, cx| {
12547        editor.backspace(&Default::default(), window, cx);
12548    });
12549    cx.run_until_parked();
12550    cx.assert_state_with_diff(
12551        indoc! {r#"
12552
12553        - two
12554        - threeˇ
12555        +
12556        "#}
12557        .to_string(),
12558    );
12559}
12560
12561#[gpui::test]
12562async fn test_deletion_reverts(cx: &mut TestAppContext) {
12563    init_test(cx, |_| {});
12564    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12565    let base_text = indoc! {r#"struct Row;
12566struct Row1;
12567struct Row2;
12568
12569struct Row4;
12570struct Row5;
12571struct Row6;
12572
12573struct Row8;
12574struct Row9;
12575struct Row10;"#};
12576
12577    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12578    assert_hunk_revert(
12579        indoc! {r#"struct Row;
12580                   struct Row2;
12581
12582                   ˇstruct Row4;
12583                   struct Row5;
12584                   struct Row6;
12585                   ˇ
12586                   struct Row8;
12587                   struct Row10;"#},
12588        vec![
12589            DiffHunkStatus::deleted_none(),
12590            DiffHunkStatus::deleted_none(),
12591        ],
12592        indoc! {r#"struct Row;
12593                   struct Row2;
12594
12595                   ˇstruct Row4;
12596                   struct Row5;
12597                   struct Row6;
12598                   ˇ
12599                   struct Row8;
12600                   struct Row10;"#},
12601        base_text,
12602        &mut cx,
12603    );
12604    assert_hunk_revert(
12605        indoc! {r#"struct Row;
12606                   struct Row2;
12607
12608                   «ˇstruct Row4;
12609                   struct» Row5;
12610                   «struct Row6;
12611                   ˇ»
12612                   struct Row8;
12613                   struct Row10;"#},
12614        vec![
12615            DiffHunkStatus::deleted_none(),
12616            DiffHunkStatus::deleted_none(),
12617        ],
12618        indoc! {r#"struct Row;
12619                   struct Row2;
12620
12621                   «ˇstruct Row4;
12622                   struct» Row5;
12623                   «struct Row6;
12624                   ˇ»
12625                   struct Row8;
12626                   struct Row10;"#},
12627        base_text,
12628        &mut cx,
12629    );
12630
12631    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12632    assert_hunk_revert(
12633        indoc! {r#"struct Row;
12634                   ˇstruct Row2;
12635
12636                   struct Row4;
12637                   struct Row5;
12638                   struct Row6;
12639
12640                   struct Row8;ˇ
12641                   struct Row10;"#},
12642        vec![
12643            DiffHunkStatus::deleted_none(),
12644            DiffHunkStatus::deleted_none(),
12645        ],
12646        indoc! {r#"struct Row;
12647                   struct Row1;
12648                   ˇstruct Row2;
12649
12650                   struct Row4;
12651                   struct Row5;
12652                   struct Row6;
12653
12654                   struct Row8;ˇ
12655                   struct Row9;
12656                   struct Row10;"#},
12657        base_text,
12658        &mut cx,
12659    );
12660    assert_hunk_revert(
12661        indoc! {r#"struct Row;
12662                   struct Row2«ˇ;
12663                   struct Row4;
12664                   struct» Row5;
12665                   «struct Row6;
12666
12667                   struct Row8;ˇ»
12668                   struct Row10;"#},
12669        vec![
12670            DiffHunkStatus::deleted_none(),
12671            DiffHunkStatus::deleted_none(),
12672            DiffHunkStatus::deleted_none(),
12673        ],
12674        indoc! {r#"struct Row;
12675                   struct Row1;
12676                   struct Row2«ˇ;
12677
12678                   struct Row4;
12679                   struct» Row5;
12680                   «struct Row6;
12681
12682                   struct Row8;ˇ»
12683                   struct Row9;
12684                   struct Row10;"#},
12685        base_text,
12686        &mut cx,
12687    );
12688}
12689
12690#[gpui::test]
12691async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
12692    init_test(cx, |_| {});
12693
12694    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12695    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12696    let base_text_3 =
12697        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12698
12699    let text_1 = edit_first_char_of_every_line(base_text_1);
12700    let text_2 = edit_first_char_of_every_line(base_text_2);
12701    let text_3 = edit_first_char_of_every_line(base_text_3);
12702
12703    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12704    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12705    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12706
12707    let multibuffer = cx.new(|cx| {
12708        let mut multibuffer = MultiBuffer::new(ReadWrite);
12709        multibuffer.push_excerpts(
12710            buffer_1.clone(),
12711            [
12712                ExcerptRange {
12713                    context: Point::new(0, 0)..Point::new(3, 0),
12714                    primary: None,
12715                },
12716                ExcerptRange {
12717                    context: Point::new(5, 0)..Point::new(7, 0),
12718                    primary: None,
12719                },
12720                ExcerptRange {
12721                    context: Point::new(9, 0)..Point::new(10, 4),
12722                    primary: None,
12723                },
12724            ],
12725            cx,
12726        );
12727        multibuffer.push_excerpts(
12728            buffer_2.clone(),
12729            [
12730                ExcerptRange {
12731                    context: Point::new(0, 0)..Point::new(3, 0),
12732                    primary: None,
12733                },
12734                ExcerptRange {
12735                    context: Point::new(5, 0)..Point::new(7, 0),
12736                    primary: None,
12737                },
12738                ExcerptRange {
12739                    context: Point::new(9, 0)..Point::new(10, 4),
12740                    primary: None,
12741                },
12742            ],
12743            cx,
12744        );
12745        multibuffer.push_excerpts(
12746            buffer_3.clone(),
12747            [
12748                ExcerptRange {
12749                    context: Point::new(0, 0)..Point::new(3, 0),
12750                    primary: None,
12751                },
12752                ExcerptRange {
12753                    context: Point::new(5, 0)..Point::new(7, 0),
12754                    primary: None,
12755                },
12756                ExcerptRange {
12757                    context: Point::new(9, 0)..Point::new(10, 4),
12758                    primary: None,
12759                },
12760            ],
12761            cx,
12762        );
12763        multibuffer
12764    });
12765
12766    let fs = FakeFs::new(cx.executor());
12767    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
12768    let (editor, cx) = cx
12769        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
12770    editor.update_in(cx, |editor, _window, cx| {
12771        for (buffer, diff_base) in [
12772            (buffer_1.clone(), base_text_1),
12773            (buffer_2.clone(), base_text_2),
12774            (buffer_3.clone(), base_text_3),
12775        ] {
12776            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12777            editor
12778                .buffer
12779                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12780        }
12781    });
12782    cx.executor().run_until_parked();
12783
12784    editor.update_in(cx, |editor, window, cx| {
12785        assert_eq!(editor.text(cx), "Xaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
12786        editor.select_all(&SelectAll, window, cx);
12787        editor.git_restore(&Default::default(), window, cx);
12788    });
12789    cx.executor().run_until_parked();
12790
12791    // When all ranges are selected, all buffer hunks are reverted.
12792    editor.update(cx, |editor, cx| {
12793        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");
12794    });
12795    buffer_1.update(cx, |buffer, _| {
12796        assert_eq!(buffer.text(), base_text_1);
12797    });
12798    buffer_2.update(cx, |buffer, _| {
12799        assert_eq!(buffer.text(), base_text_2);
12800    });
12801    buffer_3.update(cx, |buffer, _| {
12802        assert_eq!(buffer.text(), base_text_3);
12803    });
12804
12805    editor.update_in(cx, |editor, window, cx| {
12806        editor.undo(&Default::default(), window, cx);
12807    });
12808
12809    editor.update_in(cx, |editor, window, cx| {
12810        editor.change_selections(None, window, cx, |s| {
12811            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12812        });
12813        editor.git_restore(&Default::default(), window, cx);
12814    });
12815
12816    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12817    // but not affect buffer_2 and its related excerpts.
12818    editor.update(cx, |editor, cx| {
12819        assert_eq!(
12820            editor.text(cx),
12821            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
12822        );
12823    });
12824    buffer_1.update(cx, |buffer, _| {
12825        assert_eq!(buffer.text(), base_text_1);
12826    });
12827    buffer_2.update(cx, |buffer, _| {
12828        assert_eq!(
12829            buffer.text(),
12830            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12831        );
12832    });
12833    buffer_3.update(cx, |buffer, _| {
12834        assert_eq!(
12835            buffer.text(),
12836            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12837        );
12838    });
12839
12840    fn edit_first_char_of_every_line(text: &str) -> String {
12841        text.split('\n')
12842            .map(|line| format!("X{}", &line[1..]))
12843            .collect::<Vec<_>>()
12844            .join("\n")
12845    }
12846}
12847
12848#[gpui::test]
12849async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
12850    init_test(cx, |_| {});
12851
12852    let cols = 4;
12853    let rows = 10;
12854    let sample_text_1 = sample_text(rows, cols, 'a');
12855    assert_eq!(
12856        sample_text_1,
12857        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12858    );
12859    let sample_text_2 = sample_text(rows, cols, 'l');
12860    assert_eq!(
12861        sample_text_2,
12862        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12863    );
12864    let sample_text_3 = sample_text(rows, cols, 'v');
12865    assert_eq!(
12866        sample_text_3,
12867        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12868    );
12869
12870    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12871    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12872    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12873
12874    let multi_buffer = cx.new(|cx| {
12875        let mut multibuffer = MultiBuffer::new(ReadWrite);
12876        multibuffer.push_excerpts(
12877            buffer_1.clone(),
12878            [
12879                ExcerptRange {
12880                    context: Point::new(0, 0)..Point::new(3, 0),
12881                    primary: None,
12882                },
12883                ExcerptRange {
12884                    context: Point::new(5, 0)..Point::new(7, 0),
12885                    primary: None,
12886                },
12887                ExcerptRange {
12888                    context: Point::new(9, 0)..Point::new(10, 4),
12889                    primary: None,
12890                },
12891            ],
12892            cx,
12893        );
12894        multibuffer.push_excerpts(
12895            buffer_2.clone(),
12896            [
12897                ExcerptRange {
12898                    context: Point::new(0, 0)..Point::new(3, 0),
12899                    primary: None,
12900                },
12901                ExcerptRange {
12902                    context: Point::new(5, 0)..Point::new(7, 0),
12903                    primary: None,
12904                },
12905                ExcerptRange {
12906                    context: Point::new(9, 0)..Point::new(10, 4),
12907                    primary: None,
12908                },
12909            ],
12910            cx,
12911        );
12912        multibuffer.push_excerpts(
12913            buffer_3.clone(),
12914            [
12915                ExcerptRange {
12916                    context: Point::new(0, 0)..Point::new(3, 0),
12917                    primary: None,
12918                },
12919                ExcerptRange {
12920                    context: Point::new(5, 0)..Point::new(7, 0),
12921                    primary: None,
12922                },
12923                ExcerptRange {
12924                    context: Point::new(9, 0)..Point::new(10, 4),
12925                    primary: None,
12926                },
12927            ],
12928            cx,
12929        );
12930        multibuffer
12931    });
12932
12933    let fs = FakeFs::new(cx.executor());
12934    fs.insert_tree(
12935        "/a",
12936        json!({
12937            "main.rs": sample_text_1,
12938            "other.rs": sample_text_2,
12939            "lib.rs": sample_text_3,
12940        }),
12941    )
12942    .await;
12943    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12944    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12945    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12946    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12947        Editor::new(
12948            EditorMode::Full,
12949            multi_buffer,
12950            Some(project.clone()),
12951            true,
12952            window,
12953            cx,
12954        )
12955    });
12956    let multibuffer_item_id = workspace
12957        .update(cx, |workspace, window, cx| {
12958            assert!(
12959                workspace.active_item(cx).is_none(),
12960                "active item should be None before the first item is added"
12961            );
12962            workspace.add_item_to_active_pane(
12963                Box::new(multi_buffer_editor.clone()),
12964                None,
12965                true,
12966                window,
12967                cx,
12968            );
12969            let active_item = workspace
12970                .active_item(cx)
12971                .expect("should have an active item after adding the multi buffer");
12972            assert!(
12973                !active_item.is_singleton(cx),
12974                "A multi buffer was expected to active after adding"
12975            );
12976            active_item.item_id()
12977        })
12978        .unwrap();
12979    cx.executor().run_until_parked();
12980
12981    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12982        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12983            s.select_ranges(Some(1..2))
12984        });
12985        editor.open_excerpts(&OpenExcerpts, window, cx);
12986    });
12987    cx.executor().run_until_parked();
12988    let first_item_id = workspace
12989        .update(cx, |workspace, window, cx| {
12990            let active_item = workspace
12991                .active_item(cx)
12992                .expect("should have an active item after navigating into the 1st buffer");
12993            let first_item_id = active_item.item_id();
12994            assert_ne!(
12995                first_item_id, multibuffer_item_id,
12996                "Should navigate into the 1st buffer and activate it"
12997            );
12998            assert!(
12999                active_item.is_singleton(cx),
13000                "New active item should be a singleton buffer"
13001            );
13002            assert_eq!(
13003                active_item
13004                    .act_as::<Editor>(cx)
13005                    .expect("should have navigated into an editor for the 1st buffer")
13006                    .read(cx)
13007                    .text(cx),
13008                sample_text_1
13009            );
13010
13011            workspace
13012                .go_back(workspace.active_pane().downgrade(), window, cx)
13013                .detach_and_log_err(cx);
13014
13015            first_item_id
13016        })
13017        .unwrap();
13018    cx.executor().run_until_parked();
13019    workspace
13020        .update(cx, |workspace, _, cx| {
13021            let active_item = workspace
13022                .active_item(cx)
13023                .expect("should have an active item after navigating back");
13024            assert_eq!(
13025                active_item.item_id(),
13026                multibuffer_item_id,
13027                "Should navigate back to the multi buffer"
13028            );
13029            assert!(!active_item.is_singleton(cx));
13030        })
13031        .unwrap();
13032
13033    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13034        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13035            s.select_ranges(Some(39..40))
13036        });
13037        editor.open_excerpts(&OpenExcerpts, window, cx);
13038    });
13039    cx.executor().run_until_parked();
13040    let second_item_id = workspace
13041        .update(cx, |workspace, window, cx| {
13042            let active_item = workspace
13043                .active_item(cx)
13044                .expect("should have an active item after navigating into the 2nd buffer");
13045            let second_item_id = active_item.item_id();
13046            assert_ne!(
13047                second_item_id, multibuffer_item_id,
13048                "Should navigate away from the multibuffer"
13049            );
13050            assert_ne!(
13051                second_item_id, first_item_id,
13052                "Should navigate into the 2nd buffer and activate it"
13053            );
13054            assert!(
13055                active_item.is_singleton(cx),
13056                "New active item should be a singleton buffer"
13057            );
13058            assert_eq!(
13059                active_item
13060                    .act_as::<Editor>(cx)
13061                    .expect("should have navigated into an editor")
13062                    .read(cx)
13063                    .text(cx),
13064                sample_text_2
13065            );
13066
13067            workspace
13068                .go_back(workspace.active_pane().downgrade(), window, cx)
13069                .detach_and_log_err(cx);
13070
13071            second_item_id
13072        })
13073        .unwrap();
13074    cx.executor().run_until_parked();
13075    workspace
13076        .update(cx, |workspace, _, cx| {
13077            let active_item = workspace
13078                .active_item(cx)
13079                .expect("should have an active item after navigating back from the 2nd buffer");
13080            assert_eq!(
13081                active_item.item_id(),
13082                multibuffer_item_id,
13083                "Should navigate back from the 2nd buffer to the multi buffer"
13084            );
13085            assert!(!active_item.is_singleton(cx));
13086        })
13087        .unwrap();
13088
13089    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13090        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13091            s.select_ranges(Some(70..70))
13092        });
13093        editor.open_excerpts(&OpenExcerpts, window, cx);
13094    });
13095    cx.executor().run_until_parked();
13096    workspace
13097        .update(cx, |workspace, window, cx| {
13098            let active_item = workspace
13099                .active_item(cx)
13100                .expect("should have an active item after navigating into the 3rd buffer");
13101            let third_item_id = active_item.item_id();
13102            assert_ne!(
13103                third_item_id, multibuffer_item_id,
13104                "Should navigate into the 3rd buffer and activate it"
13105            );
13106            assert_ne!(third_item_id, first_item_id);
13107            assert_ne!(third_item_id, second_item_id);
13108            assert!(
13109                active_item.is_singleton(cx),
13110                "New active item should be a singleton buffer"
13111            );
13112            assert_eq!(
13113                active_item
13114                    .act_as::<Editor>(cx)
13115                    .expect("should have navigated into an editor")
13116                    .read(cx)
13117                    .text(cx),
13118                sample_text_3
13119            );
13120
13121            workspace
13122                .go_back(workspace.active_pane().downgrade(), window, cx)
13123                .detach_and_log_err(cx);
13124        })
13125        .unwrap();
13126    cx.executor().run_until_parked();
13127    workspace
13128        .update(cx, |workspace, _, cx| {
13129            let active_item = workspace
13130                .active_item(cx)
13131                .expect("should have an active item after navigating back from the 3rd buffer");
13132            assert_eq!(
13133                active_item.item_id(),
13134                multibuffer_item_id,
13135                "Should navigate back from the 3rd buffer to the multi buffer"
13136            );
13137            assert!(!active_item.is_singleton(cx));
13138        })
13139        .unwrap();
13140}
13141
13142#[gpui::test]
13143async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13144    init_test(cx, |_| {});
13145
13146    let mut cx = EditorTestContext::new(cx).await;
13147
13148    let diff_base = r#"
13149        use some::mod;
13150
13151        const A: u32 = 42;
13152
13153        fn main() {
13154            println!("hello");
13155
13156            println!("world");
13157        }
13158        "#
13159    .unindent();
13160
13161    cx.set_state(
13162        &r#"
13163        use some::modified;
13164
13165        ˇ
13166        fn main() {
13167            println!("hello there");
13168
13169            println!("around the");
13170            println!("world");
13171        }
13172        "#
13173        .unindent(),
13174    );
13175
13176    cx.set_head_text(&diff_base);
13177    executor.run_until_parked();
13178
13179    cx.update_editor(|editor, window, cx| {
13180        editor.go_to_next_hunk(&GoToHunk, window, cx);
13181        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13182    });
13183    executor.run_until_parked();
13184    cx.assert_state_with_diff(
13185        r#"
13186          use some::modified;
13187
13188
13189          fn main() {
13190        -     println!("hello");
13191        + ˇ    println!("hello there");
13192
13193              println!("around the");
13194              println!("world");
13195          }
13196        "#
13197        .unindent(),
13198    );
13199
13200    cx.update_editor(|editor, window, cx| {
13201        for _ in 0..2 {
13202            editor.go_to_next_hunk(&GoToHunk, window, cx);
13203            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13204        }
13205    });
13206    executor.run_until_parked();
13207    cx.assert_state_with_diff(
13208        r#"
13209        - use some::mod;
13210        + ˇuse some::modified;
13211
13212
13213          fn main() {
13214        -     println!("hello");
13215        +     println!("hello there");
13216
13217        +     println!("around the");
13218              println!("world");
13219          }
13220        "#
13221        .unindent(),
13222    );
13223
13224    cx.update_editor(|editor, window, cx| {
13225        editor.go_to_next_hunk(&GoToHunk, window, cx);
13226        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13227    });
13228    executor.run_until_parked();
13229    cx.assert_state_with_diff(
13230        r#"
13231        - use some::mod;
13232        + use some::modified;
13233
13234        - const A: u32 = 42;
13235          ˇ
13236          fn main() {
13237        -     println!("hello");
13238        +     println!("hello there");
13239
13240        +     println!("around the");
13241              println!("world");
13242          }
13243        "#
13244        .unindent(),
13245    );
13246
13247    cx.update_editor(|editor, window, cx| {
13248        editor.cancel(&Cancel, window, cx);
13249    });
13250
13251    cx.assert_state_with_diff(
13252        r#"
13253          use some::modified;
13254
13255          ˇ
13256          fn main() {
13257              println!("hello there");
13258
13259              println!("around the");
13260              println!("world");
13261          }
13262        "#
13263        .unindent(),
13264    );
13265}
13266
13267#[gpui::test]
13268async fn test_diff_base_change_with_expanded_diff_hunks(
13269    executor: BackgroundExecutor,
13270    cx: &mut TestAppContext,
13271) {
13272    init_test(cx, |_| {});
13273
13274    let mut cx = EditorTestContext::new(cx).await;
13275
13276    let diff_base = r#"
13277        use some::mod1;
13278        use some::mod2;
13279
13280        const A: u32 = 42;
13281        const B: u32 = 42;
13282        const C: u32 = 42;
13283
13284        fn main() {
13285            println!("hello");
13286
13287            println!("world");
13288        }
13289        "#
13290    .unindent();
13291
13292    cx.set_state(
13293        &r#"
13294        use some::mod2;
13295
13296        const A: u32 = 42;
13297        const C: u32 = 42;
13298
13299        fn main(ˇ) {
13300            //println!("hello");
13301
13302            println!("world");
13303            //
13304            //
13305        }
13306        "#
13307        .unindent(),
13308    );
13309
13310    cx.set_head_text(&diff_base);
13311    executor.run_until_parked();
13312
13313    cx.update_editor(|editor, window, cx| {
13314        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13315    });
13316    executor.run_until_parked();
13317    cx.assert_state_with_diff(
13318        r#"
13319        - use some::mod1;
13320          use some::mod2;
13321
13322          const A: u32 = 42;
13323        - const B: u32 = 42;
13324          const C: u32 = 42;
13325
13326          fn main(ˇ) {
13327        -     println!("hello");
13328        +     //println!("hello");
13329
13330              println!("world");
13331        +     //
13332        +     //
13333          }
13334        "#
13335        .unindent(),
13336    );
13337
13338    cx.set_head_text("new diff base!");
13339    executor.run_until_parked();
13340    cx.assert_state_with_diff(
13341        r#"
13342        - new diff base!
13343        + use some::mod2;
13344        +
13345        + const A: u32 = 42;
13346        + const C: u32 = 42;
13347        +
13348        + fn main(ˇ) {
13349        +     //println!("hello");
13350        +
13351        +     println!("world");
13352        +     //
13353        +     //
13354        + }
13355        "#
13356        .unindent(),
13357    );
13358}
13359
13360#[gpui::test]
13361async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13362    init_test(cx, |_| {});
13363
13364    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13365    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13366    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13367    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13368    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13369    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13370
13371    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13372    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13373    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13374
13375    let multi_buffer = cx.new(|cx| {
13376        let mut multibuffer = MultiBuffer::new(ReadWrite);
13377        multibuffer.push_excerpts(
13378            buffer_1.clone(),
13379            [
13380                ExcerptRange {
13381                    context: Point::new(0, 0)..Point::new(3, 0),
13382                    primary: None,
13383                },
13384                ExcerptRange {
13385                    context: Point::new(5, 0)..Point::new(7, 0),
13386                    primary: None,
13387                },
13388                ExcerptRange {
13389                    context: Point::new(9, 0)..Point::new(10, 3),
13390                    primary: None,
13391                },
13392            ],
13393            cx,
13394        );
13395        multibuffer.push_excerpts(
13396            buffer_2.clone(),
13397            [
13398                ExcerptRange {
13399                    context: Point::new(0, 0)..Point::new(3, 0),
13400                    primary: None,
13401                },
13402                ExcerptRange {
13403                    context: Point::new(5, 0)..Point::new(7, 0),
13404                    primary: None,
13405                },
13406                ExcerptRange {
13407                    context: Point::new(9, 0)..Point::new(10, 3),
13408                    primary: None,
13409                },
13410            ],
13411            cx,
13412        );
13413        multibuffer.push_excerpts(
13414            buffer_3.clone(),
13415            [
13416                ExcerptRange {
13417                    context: Point::new(0, 0)..Point::new(3, 0),
13418                    primary: None,
13419                },
13420                ExcerptRange {
13421                    context: Point::new(5, 0)..Point::new(7, 0),
13422                    primary: None,
13423                },
13424                ExcerptRange {
13425                    context: Point::new(9, 0)..Point::new(10, 3),
13426                    primary: None,
13427                },
13428            ],
13429            cx,
13430        );
13431        multibuffer
13432    });
13433
13434    let editor = cx.add_window(|window, cx| {
13435        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13436    });
13437    editor
13438        .update(cx, |editor, _window, cx| {
13439            for (buffer, diff_base) in [
13440                (buffer_1.clone(), file_1_old),
13441                (buffer_2.clone(), file_2_old),
13442                (buffer_3.clone(), file_3_old),
13443            ] {
13444                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13445                editor
13446                    .buffer
13447                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13448            }
13449        })
13450        .unwrap();
13451
13452    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13453    cx.run_until_parked();
13454
13455    cx.assert_editor_state(
13456        &"
13457            ˇaaa
13458            ccc
13459            ddd
13460
13461            ggg
13462            hhh
13463
13464
13465            lll
13466            mmm
13467            NNN
13468
13469            qqq
13470            rrr
13471
13472            uuu
13473            111
13474            222
13475            333
13476
13477            666
13478            777
13479
13480            000
13481            !!!"
13482        .unindent(),
13483    );
13484
13485    cx.update_editor(|editor, window, cx| {
13486        editor.select_all(&SelectAll, window, cx);
13487        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13488    });
13489    cx.executor().run_until_parked();
13490
13491    cx.assert_state_with_diff(
13492        "
13493            «aaa
13494          - bbb
13495            ccc
13496            ddd
13497
13498            ggg
13499            hhh
13500
13501
13502            lll
13503            mmm
13504          - nnn
13505          + NNN
13506
13507            qqq
13508            rrr
13509
13510            uuu
13511            111
13512            222
13513            333
13514
13515          + 666
13516            777
13517
13518            000
13519            !!!ˇ»"
13520            .unindent(),
13521    );
13522}
13523
13524#[gpui::test]
13525async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13526    init_test(cx, |_| {});
13527
13528    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13529    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13530
13531    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13532    let multi_buffer = cx.new(|cx| {
13533        let mut multibuffer = MultiBuffer::new(ReadWrite);
13534        multibuffer.push_excerpts(
13535            buffer.clone(),
13536            [
13537                ExcerptRange {
13538                    context: Point::new(0, 0)..Point::new(2, 0),
13539                    primary: None,
13540                },
13541                ExcerptRange {
13542                    context: Point::new(4, 0)..Point::new(7, 0),
13543                    primary: None,
13544                },
13545                ExcerptRange {
13546                    context: Point::new(9, 0)..Point::new(10, 0),
13547                    primary: None,
13548                },
13549            ],
13550            cx,
13551        );
13552        multibuffer
13553    });
13554
13555    let editor = cx.add_window(|window, cx| {
13556        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13557    });
13558    editor
13559        .update(cx, |editor, _window, cx| {
13560            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13561            editor
13562                .buffer
13563                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13564        })
13565        .unwrap();
13566
13567    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13568    cx.run_until_parked();
13569
13570    cx.update_editor(|editor, window, cx| {
13571        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13572    });
13573    cx.executor().run_until_parked();
13574
13575    // When the start of a hunk coincides with the start of its excerpt,
13576    // the hunk is expanded. When the start of a a hunk is earlier than
13577    // the start of its excerpt, the hunk is not expanded.
13578    cx.assert_state_with_diff(
13579        "
13580            ˇaaa
13581          - bbb
13582          + BBB
13583
13584          - ddd
13585          - eee
13586          + DDD
13587          + EEE
13588            fff
13589
13590            iii
13591        "
13592        .unindent(),
13593    );
13594}
13595
13596#[gpui::test]
13597async fn test_edits_around_expanded_insertion_hunks(
13598    executor: BackgroundExecutor,
13599    cx: &mut TestAppContext,
13600) {
13601    init_test(cx, |_| {});
13602
13603    let mut cx = EditorTestContext::new(cx).await;
13604
13605    let diff_base = r#"
13606        use some::mod1;
13607        use some::mod2;
13608
13609        const A: u32 = 42;
13610
13611        fn main() {
13612            println!("hello");
13613
13614            println!("world");
13615        }
13616        "#
13617    .unindent();
13618    executor.run_until_parked();
13619    cx.set_state(
13620        &r#"
13621        use some::mod1;
13622        use some::mod2;
13623
13624        const A: u32 = 42;
13625        const B: u32 = 42;
13626        const C: u32 = 42;
13627        ˇ
13628
13629        fn main() {
13630            println!("hello");
13631
13632            println!("world");
13633        }
13634        "#
13635        .unindent(),
13636    );
13637
13638    cx.set_head_text(&diff_base);
13639    executor.run_until_parked();
13640
13641    cx.update_editor(|editor, window, cx| {
13642        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13643    });
13644    executor.run_until_parked();
13645
13646    cx.assert_state_with_diff(
13647        r#"
13648        use some::mod1;
13649        use some::mod2;
13650
13651        const A: u32 = 42;
13652      + const B: u32 = 42;
13653      + const C: u32 = 42;
13654      + ˇ
13655
13656        fn main() {
13657            println!("hello");
13658
13659            println!("world");
13660        }
13661      "#
13662        .unindent(),
13663    );
13664
13665    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13666    executor.run_until_parked();
13667
13668    cx.assert_state_with_diff(
13669        r#"
13670        use some::mod1;
13671        use some::mod2;
13672
13673        const A: u32 = 42;
13674      + const B: u32 = 42;
13675      + const C: u32 = 42;
13676      + const D: u32 = 42;
13677      + ˇ
13678
13679        fn main() {
13680            println!("hello");
13681
13682            println!("world");
13683        }
13684      "#
13685        .unindent(),
13686    );
13687
13688    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13689    executor.run_until_parked();
13690
13691    cx.assert_state_with_diff(
13692        r#"
13693        use some::mod1;
13694        use some::mod2;
13695
13696        const A: u32 = 42;
13697      + const B: u32 = 42;
13698      + const C: u32 = 42;
13699      + const D: u32 = 42;
13700      + const E: u32 = 42;
13701      + ˇ
13702
13703        fn main() {
13704            println!("hello");
13705
13706            println!("world");
13707        }
13708      "#
13709        .unindent(),
13710    );
13711
13712    cx.update_editor(|editor, window, cx| {
13713        editor.delete_line(&DeleteLine, window, cx);
13714    });
13715    executor.run_until_parked();
13716
13717    cx.assert_state_with_diff(
13718        r#"
13719        use some::mod1;
13720        use some::mod2;
13721
13722        const A: u32 = 42;
13723      + const B: u32 = 42;
13724      + const C: u32 = 42;
13725      + const D: u32 = 42;
13726      + const E: u32 = 42;
13727        ˇ
13728        fn main() {
13729            println!("hello");
13730
13731            println!("world");
13732        }
13733      "#
13734        .unindent(),
13735    );
13736
13737    cx.update_editor(|editor, window, cx| {
13738        editor.move_up(&MoveUp, window, cx);
13739        editor.delete_line(&DeleteLine, window, cx);
13740        editor.move_up(&MoveUp, window, cx);
13741        editor.delete_line(&DeleteLine, window, cx);
13742        editor.move_up(&MoveUp, window, cx);
13743        editor.delete_line(&DeleteLine, window, cx);
13744    });
13745    executor.run_until_parked();
13746    cx.assert_state_with_diff(
13747        r#"
13748        use some::mod1;
13749        use some::mod2;
13750
13751        const A: u32 = 42;
13752      + const B: u32 = 42;
13753        ˇ
13754        fn main() {
13755            println!("hello");
13756
13757            println!("world");
13758        }
13759      "#
13760        .unindent(),
13761    );
13762
13763    cx.update_editor(|editor, window, cx| {
13764        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13765        editor.delete_line(&DeleteLine, window, cx);
13766    });
13767    executor.run_until_parked();
13768    cx.assert_state_with_diff(
13769        r#"
13770        ˇ
13771        fn main() {
13772            println!("hello");
13773
13774            println!("world");
13775        }
13776      "#
13777        .unindent(),
13778    );
13779}
13780
13781#[gpui::test]
13782async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13783    init_test(cx, |_| {});
13784
13785    let mut cx = EditorTestContext::new(cx).await;
13786    cx.set_head_text(indoc! { "
13787        one
13788        two
13789        three
13790        four
13791        five
13792        "
13793    });
13794    cx.set_state(indoc! { "
13795        one
13796        ˇthree
13797        five
13798    "});
13799    cx.run_until_parked();
13800    cx.update_editor(|editor, window, cx| {
13801        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13802    });
13803    cx.assert_state_with_diff(
13804        indoc! { "
13805        one
13806      - two
13807        ˇthree
13808      - four
13809        five
13810    "}
13811        .to_string(),
13812    );
13813    cx.update_editor(|editor, window, cx| {
13814        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13815    });
13816
13817    cx.assert_state_with_diff(
13818        indoc! { "
13819        one
13820        ˇthree
13821        five
13822    "}
13823        .to_string(),
13824    );
13825
13826    cx.set_state(indoc! { "
13827        one
13828        ˇTWO
13829        three
13830        four
13831        five
13832    "});
13833    cx.run_until_parked();
13834    cx.update_editor(|editor, window, cx| {
13835        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13836    });
13837
13838    cx.assert_state_with_diff(
13839        indoc! { "
13840            one
13841          - two
13842          + ˇTWO
13843            three
13844            four
13845            five
13846        "}
13847        .to_string(),
13848    );
13849    cx.update_editor(|editor, window, cx| {
13850        editor.move_up(&Default::default(), window, cx);
13851        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13852    });
13853    cx.assert_state_with_diff(
13854        indoc! { "
13855            one
13856            ˇTWO
13857            three
13858            four
13859            five
13860        "}
13861        .to_string(),
13862    );
13863}
13864
13865#[gpui::test]
13866async fn test_edits_around_expanded_deletion_hunks(
13867    executor: BackgroundExecutor,
13868    cx: &mut TestAppContext,
13869) {
13870    init_test(cx, |_| {});
13871
13872    let mut cx = EditorTestContext::new(cx).await;
13873
13874    let diff_base = r#"
13875        use some::mod1;
13876        use some::mod2;
13877
13878        const A: u32 = 42;
13879        const B: u32 = 42;
13880        const C: u32 = 42;
13881
13882
13883        fn main() {
13884            println!("hello");
13885
13886            println!("world");
13887        }
13888    "#
13889    .unindent();
13890    executor.run_until_parked();
13891    cx.set_state(
13892        &r#"
13893        use some::mod1;
13894        use some::mod2;
13895
13896        ˇconst B: u32 = 42;
13897        const C: u32 = 42;
13898
13899
13900        fn main() {
13901            println!("hello");
13902
13903            println!("world");
13904        }
13905        "#
13906        .unindent(),
13907    );
13908
13909    cx.set_head_text(&diff_base);
13910    executor.run_until_parked();
13911
13912    cx.update_editor(|editor, window, cx| {
13913        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13914    });
13915    executor.run_until_parked();
13916
13917    cx.assert_state_with_diff(
13918        r#"
13919        use some::mod1;
13920        use some::mod2;
13921
13922      - const A: u32 = 42;
13923        ˇconst B: u32 = 42;
13924        const C: u32 = 42;
13925
13926
13927        fn main() {
13928            println!("hello");
13929
13930            println!("world");
13931        }
13932      "#
13933        .unindent(),
13934    );
13935
13936    cx.update_editor(|editor, window, cx| {
13937        editor.delete_line(&DeleteLine, window, cx);
13938    });
13939    executor.run_until_parked();
13940    cx.assert_state_with_diff(
13941        r#"
13942        use some::mod1;
13943        use some::mod2;
13944
13945      - const A: u32 = 42;
13946      - const B: u32 = 42;
13947        ˇconst C: u32 = 42;
13948
13949
13950        fn main() {
13951            println!("hello");
13952
13953            println!("world");
13954        }
13955      "#
13956        .unindent(),
13957    );
13958
13959    cx.update_editor(|editor, window, cx| {
13960        editor.delete_line(&DeleteLine, window, cx);
13961    });
13962    executor.run_until_parked();
13963    cx.assert_state_with_diff(
13964        r#"
13965        use some::mod1;
13966        use some::mod2;
13967
13968      - const A: u32 = 42;
13969      - const B: u32 = 42;
13970      - const C: u32 = 42;
13971        ˇ
13972
13973        fn main() {
13974            println!("hello");
13975
13976            println!("world");
13977        }
13978      "#
13979        .unindent(),
13980    );
13981
13982    cx.update_editor(|editor, window, cx| {
13983        editor.handle_input("replacement", window, cx);
13984    });
13985    executor.run_until_parked();
13986    cx.assert_state_with_diff(
13987        r#"
13988        use some::mod1;
13989        use some::mod2;
13990
13991      - const A: u32 = 42;
13992      - const B: u32 = 42;
13993      - const C: u32 = 42;
13994      -
13995      + replacementˇ
13996
13997        fn main() {
13998            println!("hello");
13999
14000            println!("world");
14001        }
14002      "#
14003        .unindent(),
14004    );
14005}
14006
14007#[gpui::test]
14008async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14009    init_test(cx, |_| {});
14010
14011    let mut cx = EditorTestContext::new(cx).await;
14012
14013    let base_text = r#"
14014        one
14015        two
14016        three
14017        four
14018        five
14019    "#
14020    .unindent();
14021    executor.run_until_parked();
14022    cx.set_state(
14023        &r#"
14024        one
14025        two
14026        fˇour
14027        five
14028        "#
14029        .unindent(),
14030    );
14031
14032    cx.set_head_text(&base_text);
14033    executor.run_until_parked();
14034
14035    cx.update_editor(|editor, window, cx| {
14036        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14037    });
14038    executor.run_until_parked();
14039
14040    cx.assert_state_with_diff(
14041        r#"
14042          one
14043          two
14044        - three
14045          fˇour
14046          five
14047        "#
14048        .unindent(),
14049    );
14050
14051    cx.update_editor(|editor, window, cx| {
14052        editor.backspace(&Backspace, window, cx);
14053        editor.backspace(&Backspace, window, cx);
14054    });
14055    executor.run_until_parked();
14056    cx.assert_state_with_diff(
14057        r#"
14058          one
14059          two
14060        - threeˇ
14061        - four
14062        + our
14063          five
14064        "#
14065        .unindent(),
14066    );
14067}
14068
14069#[gpui::test]
14070async fn test_edit_after_expanded_modification_hunk(
14071    executor: BackgroundExecutor,
14072    cx: &mut TestAppContext,
14073) {
14074    init_test(cx, |_| {});
14075
14076    let mut cx = EditorTestContext::new(cx).await;
14077
14078    let diff_base = r#"
14079        use some::mod1;
14080        use some::mod2;
14081
14082        const A: u32 = 42;
14083        const B: u32 = 42;
14084        const C: u32 = 42;
14085        const D: u32 = 42;
14086
14087
14088        fn main() {
14089            println!("hello");
14090
14091            println!("world");
14092        }"#
14093    .unindent();
14094
14095    cx.set_state(
14096        &r#"
14097        use some::mod1;
14098        use some::mod2;
14099
14100        const A: u32 = 42;
14101        const B: u32 = 42;
14102        const C: u32 = 43ˇ
14103        const D: u32 = 42;
14104
14105
14106        fn main() {
14107            println!("hello");
14108
14109            println!("world");
14110        }"#
14111        .unindent(),
14112    );
14113
14114    cx.set_head_text(&diff_base);
14115    executor.run_until_parked();
14116    cx.update_editor(|editor, window, cx| {
14117        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14118    });
14119    executor.run_until_parked();
14120
14121    cx.assert_state_with_diff(
14122        r#"
14123        use some::mod1;
14124        use some::mod2;
14125
14126        const A: u32 = 42;
14127        const B: u32 = 42;
14128      - const C: u32 = 42;
14129      + const C: u32 = 43ˇ
14130        const D: u32 = 42;
14131
14132
14133        fn main() {
14134            println!("hello");
14135
14136            println!("world");
14137        }"#
14138        .unindent(),
14139    );
14140
14141    cx.update_editor(|editor, window, cx| {
14142        editor.handle_input("\nnew_line\n", window, cx);
14143    });
14144    executor.run_until_parked();
14145
14146    cx.assert_state_with_diff(
14147        r#"
14148        use some::mod1;
14149        use some::mod2;
14150
14151        const A: u32 = 42;
14152        const B: u32 = 42;
14153      - const C: u32 = 42;
14154      + const C: u32 = 43
14155      + new_line
14156      + ˇ
14157        const D: u32 = 42;
14158
14159
14160        fn main() {
14161            println!("hello");
14162
14163            println!("world");
14164        }"#
14165        .unindent(),
14166    );
14167}
14168
14169#[gpui::test]
14170async fn test_stage_and_unstage_added_file_hunk(
14171    executor: BackgroundExecutor,
14172    cx: &mut TestAppContext,
14173) {
14174    init_test(cx, |_| {});
14175
14176    let mut cx = EditorTestContext::new(cx).await;
14177    cx.update_editor(|editor, _, cx| {
14178        editor.set_expand_all_diff_hunks(cx);
14179    });
14180
14181    let working_copy = r#"
14182            ˇfn main() {
14183                println!("hello, world!");
14184            }
14185        "#
14186    .unindent();
14187
14188    cx.set_state(&working_copy);
14189    executor.run_until_parked();
14190
14191    cx.assert_state_with_diff(
14192        r#"
14193            + ˇfn main() {
14194            +     println!("hello, world!");
14195            + }
14196        "#
14197        .unindent(),
14198    );
14199    cx.assert_index_text(None);
14200
14201    cx.update_editor(|editor, window, cx| {
14202        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14203    });
14204    executor.run_until_parked();
14205    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14206    cx.assert_state_with_diff(
14207        r#"
14208            + ˇfn main() {
14209            +     println!("hello, world!");
14210            + }
14211        "#
14212        .unindent(),
14213    );
14214
14215    cx.update_editor(|editor, window, cx| {
14216        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14217    });
14218    executor.run_until_parked();
14219    cx.assert_index_text(None);
14220}
14221
14222async fn setup_indent_guides_editor(
14223    text: &str,
14224    cx: &mut TestAppContext,
14225) -> (BufferId, EditorTestContext) {
14226    init_test(cx, |_| {});
14227
14228    let mut cx = EditorTestContext::new(cx).await;
14229
14230    let buffer_id = cx.update_editor(|editor, window, cx| {
14231        editor.set_text(text, window, cx);
14232        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14233
14234        buffer_ids[0]
14235    });
14236
14237    (buffer_id, cx)
14238}
14239
14240fn assert_indent_guides(
14241    range: Range<u32>,
14242    expected: Vec<IndentGuide>,
14243    active_indices: Option<Vec<usize>>,
14244    cx: &mut EditorTestContext,
14245) {
14246    let indent_guides = cx.update_editor(|editor, window, cx| {
14247        let snapshot = editor.snapshot(window, cx).display_snapshot;
14248        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14249            editor,
14250            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14251            true,
14252            &snapshot,
14253            cx,
14254        );
14255
14256        indent_guides.sort_by(|a, b| {
14257            a.depth.cmp(&b.depth).then(
14258                a.start_row
14259                    .cmp(&b.start_row)
14260                    .then(a.end_row.cmp(&b.end_row)),
14261            )
14262        });
14263        indent_guides
14264    });
14265
14266    if let Some(expected) = active_indices {
14267        let active_indices = cx.update_editor(|editor, window, cx| {
14268            let snapshot = editor.snapshot(window, cx).display_snapshot;
14269            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14270        });
14271
14272        assert_eq!(
14273            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14274            expected,
14275            "Active indent guide indices do not match"
14276        );
14277    }
14278
14279    assert_eq!(indent_guides, expected, "Indent guides do not match");
14280}
14281
14282fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14283    IndentGuide {
14284        buffer_id,
14285        start_row: MultiBufferRow(start_row),
14286        end_row: MultiBufferRow(end_row),
14287        depth,
14288        tab_size: 4,
14289        settings: IndentGuideSettings {
14290            enabled: true,
14291            line_width: 1,
14292            active_line_width: 1,
14293            ..Default::default()
14294        },
14295    }
14296}
14297
14298#[gpui::test]
14299async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14300    let (buffer_id, mut cx) = setup_indent_guides_editor(
14301        &"
14302    fn main() {
14303        let a = 1;
14304    }"
14305        .unindent(),
14306        cx,
14307    )
14308    .await;
14309
14310    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14311}
14312
14313#[gpui::test]
14314async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14315    let (buffer_id, mut cx) = setup_indent_guides_editor(
14316        &"
14317    fn main() {
14318        let a = 1;
14319        let b = 2;
14320    }"
14321        .unindent(),
14322        cx,
14323    )
14324    .await;
14325
14326    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14327}
14328
14329#[gpui::test]
14330async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14331    let (buffer_id, mut cx) = setup_indent_guides_editor(
14332        &"
14333    fn main() {
14334        let a = 1;
14335        if a == 3 {
14336            let b = 2;
14337        } else {
14338            let c = 3;
14339        }
14340    }"
14341        .unindent(),
14342        cx,
14343    )
14344    .await;
14345
14346    assert_indent_guides(
14347        0..8,
14348        vec![
14349            indent_guide(buffer_id, 1, 6, 0),
14350            indent_guide(buffer_id, 3, 3, 1),
14351            indent_guide(buffer_id, 5, 5, 1),
14352        ],
14353        None,
14354        &mut cx,
14355    );
14356}
14357
14358#[gpui::test]
14359async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14360    let (buffer_id, mut cx) = setup_indent_guides_editor(
14361        &"
14362    fn main() {
14363        let a = 1;
14364            let b = 2;
14365        let c = 3;
14366    }"
14367        .unindent(),
14368        cx,
14369    )
14370    .await;
14371
14372    assert_indent_guides(
14373        0..5,
14374        vec![
14375            indent_guide(buffer_id, 1, 3, 0),
14376            indent_guide(buffer_id, 2, 2, 1),
14377        ],
14378        None,
14379        &mut cx,
14380    );
14381}
14382
14383#[gpui::test]
14384async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14385    let (buffer_id, mut cx) = setup_indent_guides_editor(
14386        &"
14387        fn main() {
14388            let a = 1;
14389
14390            let c = 3;
14391        }"
14392        .unindent(),
14393        cx,
14394    )
14395    .await;
14396
14397    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14398}
14399
14400#[gpui::test]
14401async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14402    let (buffer_id, mut cx) = setup_indent_guides_editor(
14403        &"
14404        fn main() {
14405            let a = 1;
14406
14407            let c = 3;
14408
14409            if a == 3 {
14410                let b = 2;
14411            } else {
14412                let c = 3;
14413            }
14414        }"
14415        .unindent(),
14416        cx,
14417    )
14418    .await;
14419
14420    assert_indent_guides(
14421        0..11,
14422        vec![
14423            indent_guide(buffer_id, 1, 9, 0),
14424            indent_guide(buffer_id, 6, 6, 1),
14425            indent_guide(buffer_id, 8, 8, 1),
14426        ],
14427        None,
14428        &mut cx,
14429    );
14430}
14431
14432#[gpui::test]
14433async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14434    let (buffer_id, mut cx) = setup_indent_guides_editor(
14435        &"
14436        fn main() {
14437            let a = 1;
14438
14439            let c = 3;
14440
14441            if a == 3 {
14442                let b = 2;
14443            } else {
14444                let c = 3;
14445            }
14446        }"
14447        .unindent(),
14448        cx,
14449    )
14450    .await;
14451
14452    assert_indent_guides(
14453        1..11,
14454        vec![
14455            indent_guide(buffer_id, 1, 9, 0),
14456            indent_guide(buffer_id, 6, 6, 1),
14457            indent_guide(buffer_id, 8, 8, 1),
14458        ],
14459        None,
14460        &mut cx,
14461    );
14462}
14463
14464#[gpui::test]
14465async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14466    let (buffer_id, mut cx) = setup_indent_guides_editor(
14467        &"
14468        fn main() {
14469            let a = 1;
14470
14471            let c = 3;
14472
14473            if a == 3 {
14474                let b = 2;
14475            } else {
14476                let c = 3;
14477            }
14478        }"
14479        .unindent(),
14480        cx,
14481    )
14482    .await;
14483
14484    assert_indent_guides(
14485        1..10,
14486        vec![
14487            indent_guide(buffer_id, 1, 9, 0),
14488            indent_guide(buffer_id, 6, 6, 1),
14489            indent_guide(buffer_id, 8, 8, 1),
14490        ],
14491        None,
14492        &mut cx,
14493    );
14494}
14495
14496#[gpui::test]
14497async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14498    let (buffer_id, mut cx) = setup_indent_guides_editor(
14499        &"
14500        block1
14501            block2
14502                block3
14503                    block4
14504            block2
14505        block1
14506        block1"
14507            .unindent(),
14508        cx,
14509    )
14510    .await;
14511
14512    assert_indent_guides(
14513        1..10,
14514        vec![
14515            indent_guide(buffer_id, 1, 4, 0),
14516            indent_guide(buffer_id, 2, 3, 1),
14517            indent_guide(buffer_id, 3, 3, 2),
14518        ],
14519        None,
14520        &mut cx,
14521    );
14522}
14523
14524#[gpui::test]
14525async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14526    let (buffer_id, mut cx) = setup_indent_guides_editor(
14527        &"
14528        block1
14529            block2
14530                block3
14531
14532        block1
14533        block1"
14534            .unindent(),
14535        cx,
14536    )
14537    .await;
14538
14539    assert_indent_guides(
14540        0..6,
14541        vec![
14542            indent_guide(buffer_id, 1, 2, 0),
14543            indent_guide(buffer_id, 2, 2, 1),
14544        ],
14545        None,
14546        &mut cx,
14547    );
14548}
14549
14550#[gpui::test]
14551async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14552    let (buffer_id, mut cx) = setup_indent_guides_editor(
14553        &"
14554        block1
14555
14556
14557
14558            block2
14559        "
14560        .unindent(),
14561        cx,
14562    )
14563    .await;
14564
14565    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14566}
14567
14568#[gpui::test]
14569async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14570    let (buffer_id, mut cx) = setup_indent_guides_editor(
14571        &"
14572        def a:
14573        \tb = 3
14574        \tif True:
14575        \t\tc = 4
14576        \t\td = 5
14577        \tprint(b)
14578        "
14579        .unindent(),
14580        cx,
14581    )
14582    .await;
14583
14584    assert_indent_guides(
14585        0..6,
14586        vec![
14587            indent_guide(buffer_id, 1, 6, 0),
14588            indent_guide(buffer_id, 3, 4, 1),
14589        ],
14590        None,
14591        &mut cx,
14592    );
14593}
14594
14595#[gpui::test]
14596async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14597    let (buffer_id, mut cx) = setup_indent_guides_editor(
14598        &"
14599    fn main() {
14600        let a = 1;
14601    }"
14602        .unindent(),
14603        cx,
14604    )
14605    .await;
14606
14607    cx.update_editor(|editor, window, cx| {
14608        editor.change_selections(None, window, cx, |s| {
14609            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14610        });
14611    });
14612
14613    assert_indent_guides(
14614        0..3,
14615        vec![indent_guide(buffer_id, 1, 1, 0)],
14616        Some(vec![0]),
14617        &mut cx,
14618    );
14619}
14620
14621#[gpui::test]
14622async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14623    let (buffer_id, mut cx) = setup_indent_guides_editor(
14624        &"
14625    fn main() {
14626        if 1 == 2 {
14627            let a = 1;
14628        }
14629    }"
14630        .unindent(),
14631        cx,
14632    )
14633    .await;
14634
14635    cx.update_editor(|editor, window, cx| {
14636        editor.change_selections(None, window, cx, |s| {
14637            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14638        });
14639    });
14640
14641    assert_indent_guides(
14642        0..4,
14643        vec![
14644            indent_guide(buffer_id, 1, 3, 0),
14645            indent_guide(buffer_id, 2, 2, 1),
14646        ],
14647        Some(vec![1]),
14648        &mut cx,
14649    );
14650
14651    cx.update_editor(|editor, window, cx| {
14652        editor.change_selections(None, window, cx, |s| {
14653            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14654        });
14655    });
14656
14657    assert_indent_guides(
14658        0..4,
14659        vec![
14660            indent_guide(buffer_id, 1, 3, 0),
14661            indent_guide(buffer_id, 2, 2, 1),
14662        ],
14663        Some(vec![1]),
14664        &mut cx,
14665    );
14666
14667    cx.update_editor(|editor, window, cx| {
14668        editor.change_selections(None, window, cx, |s| {
14669            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14670        });
14671    });
14672
14673    assert_indent_guides(
14674        0..4,
14675        vec![
14676            indent_guide(buffer_id, 1, 3, 0),
14677            indent_guide(buffer_id, 2, 2, 1),
14678        ],
14679        Some(vec![0]),
14680        &mut cx,
14681    );
14682}
14683
14684#[gpui::test]
14685async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
14686    let (buffer_id, mut cx) = setup_indent_guides_editor(
14687        &"
14688    fn main() {
14689        let a = 1;
14690
14691        let b = 2;
14692    }"
14693        .unindent(),
14694        cx,
14695    )
14696    .await;
14697
14698    cx.update_editor(|editor, window, cx| {
14699        editor.change_selections(None, window, cx, |s| {
14700            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14701        });
14702    });
14703
14704    assert_indent_guides(
14705        0..5,
14706        vec![indent_guide(buffer_id, 1, 3, 0)],
14707        Some(vec![0]),
14708        &mut cx,
14709    );
14710}
14711
14712#[gpui::test]
14713async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
14714    let (buffer_id, mut cx) = setup_indent_guides_editor(
14715        &"
14716    def m:
14717        a = 1
14718        pass"
14719            .unindent(),
14720        cx,
14721    )
14722    .await;
14723
14724    cx.update_editor(|editor, window, cx| {
14725        editor.change_selections(None, window, cx, |s| {
14726            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14727        });
14728    });
14729
14730    assert_indent_guides(
14731        0..3,
14732        vec![indent_guide(buffer_id, 1, 2, 0)],
14733        Some(vec![0]),
14734        &mut cx,
14735    );
14736}
14737
14738#[gpui::test]
14739async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
14740    init_test(cx, |_| {});
14741    let mut cx = EditorTestContext::new(cx).await;
14742    let text = indoc! {
14743        "
14744        impl A {
14745            fn b() {
14746                0;
14747                3;
14748                5;
14749                6;
14750                7;
14751            }
14752        }
14753        "
14754    };
14755    let base_text = indoc! {
14756        "
14757        impl A {
14758            fn b() {
14759                0;
14760                1;
14761                2;
14762                3;
14763                4;
14764            }
14765            fn c() {
14766                5;
14767                6;
14768                7;
14769            }
14770        }
14771        "
14772    };
14773
14774    cx.update_editor(|editor, window, cx| {
14775        editor.set_text(text, window, cx);
14776
14777        editor.buffer().update(cx, |multibuffer, cx| {
14778            let buffer = multibuffer.as_singleton().unwrap();
14779            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14780
14781            multibuffer.set_all_diff_hunks_expanded(cx);
14782            multibuffer.add_diff(diff, cx);
14783
14784            buffer.read(cx).remote_id()
14785        })
14786    });
14787    cx.run_until_parked();
14788
14789    cx.assert_state_with_diff(
14790        indoc! { "
14791          impl A {
14792              fn b() {
14793                  0;
14794        -         1;
14795        -         2;
14796                  3;
14797        -         4;
14798        -     }
14799        -     fn c() {
14800                  5;
14801                  6;
14802                  7;
14803              }
14804          }
14805          ˇ"
14806        }
14807        .to_string(),
14808    );
14809
14810    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14811        editor
14812            .snapshot(window, cx)
14813            .buffer_snapshot
14814            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14815            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14816            .collect::<Vec<_>>()
14817    });
14818    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14819    assert_eq!(
14820        actual_guides,
14821        vec![
14822            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14823            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14824            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14825        ]
14826    );
14827}
14828
14829#[gpui::test]
14830async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14831    init_test(cx, |_| {});
14832    let mut cx = EditorTestContext::new(cx).await;
14833
14834    let diff_base = r#"
14835        a
14836        b
14837        c
14838        "#
14839    .unindent();
14840
14841    cx.set_state(
14842        &r#"
14843        ˇA
14844        b
14845        C
14846        "#
14847        .unindent(),
14848    );
14849    cx.set_head_text(&diff_base);
14850    cx.update_editor(|editor, window, cx| {
14851        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14852    });
14853    executor.run_until_parked();
14854
14855    let both_hunks_expanded = r#"
14856        - a
14857        + ˇA
14858          b
14859        - c
14860        + C
14861        "#
14862    .unindent();
14863
14864    cx.assert_state_with_diff(both_hunks_expanded.clone());
14865
14866    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14867        let snapshot = editor.snapshot(window, cx);
14868        let hunks = editor
14869            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14870            .collect::<Vec<_>>();
14871        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14872        let buffer_id = hunks[0].buffer_id;
14873        hunks
14874            .into_iter()
14875            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14876            .collect::<Vec<_>>()
14877    });
14878    assert_eq!(hunk_ranges.len(), 2);
14879
14880    cx.update_editor(|editor, _, cx| {
14881        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14882    });
14883    executor.run_until_parked();
14884
14885    let second_hunk_expanded = r#"
14886          ˇA
14887          b
14888        - c
14889        + C
14890        "#
14891    .unindent();
14892
14893    cx.assert_state_with_diff(second_hunk_expanded);
14894
14895    cx.update_editor(|editor, _, cx| {
14896        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14897    });
14898    executor.run_until_parked();
14899
14900    cx.assert_state_with_diff(both_hunks_expanded.clone());
14901
14902    cx.update_editor(|editor, _, cx| {
14903        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14904    });
14905    executor.run_until_parked();
14906
14907    let first_hunk_expanded = r#"
14908        - a
14909        + ˇA
14910          b
14911          C
14912        "#
14913    .unindent();
14914
14915    cx.assert_state_with_diff(first_hunk_expanded);
14916
14917    cx.update_editor(|editor, _, cx| {
14918        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14919    });
14920    executor.run_until_parked();
14921
14922    cx.assert_state_with_diff(both_hunks_expanded);
14923
14924    cx.set_state(
14925        &r#"
14926        ˇA
14927        b
14928        "#
14929        .unindent(),
14930    );
14931    cx.run_until_parked();
14932
14933    // TODO this cursor position seems bad
14934    cx.assert_state_with_diff(
14935        r#"
14936        - ˇa
14937        + A
14938          b
14939        "#
14940        .unindent(),
14941    );
14942
14943    cx.update_editor(|editor, window, cx| {
14944        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14945    });
14946
14947    cx.assert_state_with_diff(
14948        r#"
14949            - ˇa
14950            + A
14951              b
14952            - c
14953            "#
14954        .unindent(),
14955    );
14956
14957    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14958        let snapshot = editor.snapshot(window, cx);
14959        let hunks = editor
14960            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14961            .collect::<Vec<_>>();
14962        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14963        let buffer_id = hunks[0].buffer_id;
14964        hunks
14965            .into_iter()
14966            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14967            .collect::<Vec<_>>()
14968    });
14969    assert_eq!(hunk_ranges.len(), 2);
14970
14971    cx.update_editor(|editor, _, cx| {
14972        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14973    });
14974    executor.run_until_parked();
14975
14976    cx.assert_state_with_diff(
14977        r#"
14978        - ˇa
14979        + A
14980          b
14981        "#
14982        .unindent(),
14983    );
14984}
14985
14986#[gpui::test]
14987async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
14988    init_test(cx, |_| {});
14989
14990    let mut cx = EditorTestContext::new(cx).await;
14991    cx.set_head_text(indoc! { "
14992        one
14993        two
14994        three
14995        four
14996        five
14997        "
14998    });
14999    cx.set_index_text(indoc! { "
15000        one
15001        two
15002        three
15003        four
15004        five
15005        "
15006    });
15007    cx.set_state(indoc! {"
15008        one
15009        TWO
15010        ˇTHREE
15011        FOUR
15012        five
15013    "});
15014    cx.run_until_parked();
15015    cx.update_editor(|editor, window, cx| {
15016        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15017    });
15018    cx.run_until_parked();
15019    cx.assert_index_text(Some(indoc! {"
15020        one
15021        TWO
15022        THREE
15023        FOUR
15024        five
15025    "}));
15026    cx.set_state(indoc! { "
15027        one
15028        TWO
15029        ˇTHREE-HUNDRED
15030        FOUR
15031        five
15032    "});
15033    cx.run_until_parked();
15034    cx.update_editor(|editor, window, cx| {
15035        let snapshot = editor.snapshot(window, cx);
15036        let hunks = editor
15037            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15038            .collect::<Vec<_>>();
15039        assert_eq!(hunks.len(), 1);
15040        assert_eq!(
15041            hunks[0].status(),
15042            DiffHunkStatus {
15043                kind: DiffHunkStatusKind::Modified,
15044                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15045            }
15046        );
15047
15048        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15049    });
15050    cx.run_until_parked();
15051    cx.assert_index_text(Some(indoc! {"
15052        one
15053        TWO
15054        THREE-HUNDRED
15055        FOUR
15056        five
15057    "}));
15058}
15059
15060#[gpui::test]
15061fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15062    init_test(cx, |_| {});
15063
15064    let editor = cx.add_window(|window, cx| {
15065        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15066        build_editor(buffer, window, cx)
15067    });
15068
15069    let render_args = Arc::new(Mutex::new(None));
15070    let snapshot = editor
15071        .update(cx, |editor, window, cx| {
15072            let snapshot = editor.buffer().read(cx).snapshot(cx);
15073            let range =
15074                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15075
15076            struct RenderArgs {
15077                row: MultiBufferRow,
15078                folded: bool,
15079                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15080            }
15081
15082            let crease = Crease::inline(
15083                range,
15084                FoldPlaceholder::test(),
15085                {
15086                    let toggle_callback = render_args.clone();
15087                    move |row, folded, callback, _window, _cx| {
15088                        *toggle_callback.lock() = Some(RenderArgs {
15089                            row,
15090                            folded,
15091                            callback,
15092                        });
15093                        div()
15094                    }
15095                },
15096                |_row, _folded, _window, _cx| div(),
15097            );
15098
15099            editor.insert_creases(Some(crease), cx);
15100            let snapshot = editor.snapshot(window, cx);
15101            let _div = snapshot.render_crease_toggle(
15102                MultiBufferRow(1),
15103                false,
15104                cx.entity().clone(),
15105                window,
15106                cx,
15107            );
15108            snapshot
15109        })
15110        .unwrap();
15111
15112    let render_args = render_args.lock().take().unwrap();
15113    assert_eq!(render_args.row, MultiBufferRow(1));
15114    assert!(!render_args.folded);
15115    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15116
15117    cx.update_window(*editor, |_, window, cx| {
15118        (render_args.callback)(true, window, cx)
15119    })
15120    .unwrap();
15121    let snapshot = editor
15122        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15123        .unwrap();
15124    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15125
15126    cx.update_window(*editor, |_, window, cx| {
15127        (render_args.callback)(false, window, cx)
15128    })
15129    .unwrap();
15130    let snapshot = editor
15131        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15132        .unwrap();
15133    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15134}
15135
15136#[gpui::test]
15137async fn test_input_text(cx: &mut TestAppContext) {
15138    init_test(cx, |_| {});
15139    let mut cx = EditorTestContext::new(cx).await;
15140
15141    cx.set_state(
15142        &r#"ˇone
15143        two
15144
15145        three
15146        fourˇ
15147        five
15148
15149        siˇx"#
15150            .unindent(),
15151    );
15152
15153    cx.dispatch_action(HandleInput(String::new()));
15154    cx.assert_editor_state(
15155        &r#"ˇone
15156        two
15157
15158        three
15159        fourˇ
15160        five
15161
15162        siˇx"#
15163            .unindent(),
15164    );
15165
15166    cx.dispatch_action(HandleInput("AAAA".to_string()));
15167    cx.assert_editor_state(
15168        &r#"AAAAˇone
15169        two
15170
15171        three
15172        fourAAAAˇ
15173        five
15174
15175        siAAAAˇx"#
15176            .unindent(),
15177    );
15178}
15179
15180#[gpui::test]
15181async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15182    init_test(cx, |_| {});
15183
15184    let mut cx = EditorTestContext::new(cx).await;
15185    cx.set_state(
15186        r#"let foo = 1;
15187let foo = 2;
15188let foo = 3;
15189let fooˇ = 4;
15190let foo = 5;
15191let foo = 6;
15192let foo = 7;
15193let foo = 8;
15194let foo = 9;
15195let foo = 10;
15196let foo = 11;
15197let foo = 12;
15198let foo = 13;
15199let foo = 14;
15200let foo = 15;"#,
15201    );
15202
15203    cx.update_editor(|e, window, cx| {
15204        assert_eq!(
15205            e.next_scroll_position,
15206            NextScrollCursorCenterTopBottom::Center,
15207            "Default next scroll direction is center",
15208        );
15209
15210        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15211        assert_eq!(
15212            e.next_scroll_position,
15213            NextScrollCursorCenterTopBottom::Top,
15214            "After center, next scroll direction should be top",
15215        );
15216
15217        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15218        assert_eq!(
15219            e.next_scroll_position,
15220            NextScrollCursorCenterTopBottom::Bottom,
15221            "After top, next scroll direction should be bottom",
15222        );
15223
15224        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15225        assert_eq!(
15226            e.next_scroll_position,
15227            NextScrollCursorCenterTopBottom::Center,
15228            "After bottom, scrolling should start over",
15229        );
15230
15231        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15232        assert_eq!(
15233            e.next_scroll_position,
15234            NextScrollCursorCenterTopBottom::Top,
15235            "Scrolling continues if retriggered fast enough"
15236        );
15237    });
15238
15239    cx.executor()
15240        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15241    cx.executor().run_until_parked();
15242    cx.update_editor(|e, _, _| {
15243        assert_eq!(
15244            e.next_scroll_position,
15245            NextScrollCursorCenterTopBottom::Center,
15246            "If scrolling is not triggered fast enough, it should reset"
15247        );
15248    });
15249}
15250
15251#[gpui::test]
15252async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15253    init_test(cx, |_| {});
15254    let mut cx = EditorLspTestContext::new_rust(
15255        lsp::ServerCapabilities {
15256            definition_provider: Some(lsp::OneOf::Left(true)),
15257            references_provider: Some(lsp::OneOf::Left(true)),
15258            ..lsp::ServerCapabilities::default()
15259        },
15260        cx,
15261    )
15262    .await;
15263
15264    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15265        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15266            move |params, _| async move {
15267                if empty_go_to_definition {
15268                    Ok(None)
15269                } else {
15270                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15271                        uri: params.text_document_position_params.text_document.uri,
15272                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15273                    })))
15274                }
15275            },
15276        );
15277        let references =
15278            cx.lsp
15279                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15280                    Ok(Some(vec![lsp::Location {
15281                        uri: params.text_document_position.text_document.uri,
15282                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15283                    }]))
15284                });
15285        (go_to_definition, references)
15286    };
15287
15288    cx.set_state(
15289        &r#"fn one() {
15290            let mut a = ˇtwo();
15291        }
15292
15293        fn two() {}"#
15294            .unindent(),
15295    );
15296    set_up_lsp_handlers(false, &mut cx);
15297    let navigated = cx
15298        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15299        .await
15300        .expect("Failed to navigate to definition");
15301    assert_eq!(
15302        navigated,
15303        Navigated::Yes,
15304        "Should have navigated to definition from the GetDefinition response"
15305    );
15306    cx.assert_editor_state(
15307        &r#"fn one() {
15308            let mut a = two();
15309        }
15310
15311        fn «twoˇ»() {}"#
15312            .unindent(),
15313    );
15314
15315    let editors = cx.update_workspace(|workspace, _, cx| {
15316        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15317    });
15318    cx.update_editor(|_, _, test_editor_cx| {
15319        assert_eq!(
15320            editors.len(),
15321            1,
15322            "Initially, only one, test, editor should be open in the workspace"
15323        );
15324        assert_eq!(
15325            test_editor_cx.entity(),
15326            editors.last().expect("Asserted len is 1").clone()
15327        );
15328    });
15329
15330    set_up_lsp_handlers(true, &mut cx);
15331    let navigated = cx
15332        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15333        .await
15334        .expect("Failed to navigate to lookup references");
15335    assert_eq!(
15336        navigated,
15337        Navigated::Yes,
15338        "Should have navigated to references as a fallback after empty GoToDefinition response"
15339    );
15340    // We should not change the selections in the existing file,
15341    // if opening another milti buffer with the references
15342    cx.assert_editor_state(
15343        &r#"fn one() {
15344            let mut a = two();
15345        }
15346
15347        fn «twoˇ»() {}"#
15348            .unindent(),
15349    );
15350    let editors = cx.update_workspace(|workspace, _, cx| {
15351        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15352    });
15353    cx.update_editor(|_, _, test_editor_cx| {
15354        assert_eq!(
15355            editors.len(),
15356            2,
15357            "After falling back to references search, we open a new editor with the results"
15358        );
15359        let references_fallback_text = editors
15360            .into_iter()
15361            .find(|new_editor| *new_editor != test_editor_cx.entity())
15362            .expect("Should have one non-test editor now")
15363            .read(test_editor_cx)
15364            .text(test_editor_cx);
15365        assert_eq!(
15366            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15367            "Should use the range from the references response and not the GoToDefinition one"
15368        );
15369    });
15370}
15371
15372#[gpui::test]
15373async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15374    init_test(cx, |_| {});
15375
15376    let language = Arc::new(Language::new(
15377        LanguageConfig::default(),
15378        Some(tree_sitter_rust::LANGUAGE.into()),
15379    ));
15380
15381    let text = r#"
15382        #[cfg(test)]
15383        mod tests() {
15384            #[test]
15385            fn runnable_1() {
15386                let a = 1;
15387            }
15388
15389            #[test]
15390            fn runnable_2() {
15391                let a = 1;
15392                let b = 2;
15393            }
15394        }
15395    "#
15396    .unindent();
15397
15398    let fs = FakeFs::new(cx.executor());
15399    fs.insert_file("/file.rs", Default::default()).await;
15400
15401    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15402    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15403    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15404    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15405    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15406
15407    let editor = cx.new_window_entity(|window, cx| {
15408        Editor::new(
15409            EditorMode::Full,
15410            multi_buffer,
15411            Some(project.clone()),
15412            true,
15413            window,
15414            cx,
15415        )
15416    });
15417
15418    editor.update_in(cx, |editor, window, cx| {
15419        let snapshot = editor.buffer().read(cx).snapshot(cx);
15420        editor.tasks.insert(
15421            (buffer.read(cx).remote_id(), 3),
15422            RunnableTasks {
15423                templates: vec![],
15424                offset: snapshot.anchor_before(43),
15425                column: 0,
15426                extra_variables: HashMap::default(),
15427                context_range: BufferOffset(43)..BufferOffset(85),
15428            },
15429        );
15430        editor.tasks.insert(
15431            (buffer.read(cx).remote_id(), 8),
15432            RunnableTasks {
15433                templates: vec![],
15434                offset: snapshot.anchor_before(86),
15435                column: 0,
15436                extra_variables: HashMap::default(),
15437                context_range: BufferOffset(86)..BufferOffset(191),
15438            },
15439        );
15440
15441        // Test finding task when cursor is inside function body
15442        editor.change_selections(None, window, cx, |s| {
15443            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15444        });
15445        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15446        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15447
15448        // Test finding task when cursor is on function name
15449        editor.change_selections(None, window, cx, |s| {
15450            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15451        });
15452        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15453        assert_eq!(row, 8, "Should find task when cursor is on function name");
15454    });
15455}
15456
15457#[gpui::test]
15458async fn test_multi_buffer_folding(cx: &mut TestAppContext) {
15459    init_test(cx, |_| {});
15460
15461    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15462    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15463    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15464
15465    let fs = FakeFs::new(cx.executor());
15466    fs.insert_tree(
15467        path!("/a"),
15468        json!({
15469            "first.rs": sample_text_1,
15470            "second.rs": sample_text_2,
15471            "third.rs": sample_text_3,
15472        }),
15473    )
15474    .await;
15475    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15476    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15477    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15478    let worktree = project.update(cx, |project, cx| {
15479        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15480        assert_eq!(worktrees.len(), 1);
15481        worktrees.pop().unwrap()
15482    });
15483    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15484
15485    let buffer_1 = project
15486        .update(cx, |project, cx| {
15487            project.open_buffer((worktree_id, "first.rs"), cx)
15488        })
15489        .await
15490        .unwrap();
15491    let buffer_2 = project
15492        .update(cx, |project, cx| {
15493            project.open_buffer((worktree_id, "second.rs"), cx)
15494        })
15495        .await
15496        .unwrap();
15497    let buffer_3 = project
15498        .update(cx, |project, cx| {
15499            project.open_buffer((worktree_id, "third.rs"), cx)
15500        })
15501        .await
15502        .unwrap();
15503
15504    let multi_buffer = cx.new(|cx| {
15505        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15506        multi_buffer.push_excerpts(
15507            buffer_1.clone(),
15508            [
15509                ExcerptRange {
15510                    context: Point::new(0, 0)..Point::new(3, 0),
15511                    primary: None,
15512                },
15513                ExcerptRange {
15514                    context: Point::new(5, 0)..Point::new(7, 0),
15515                    primary: None,
15516                },
15517                ExcerptRange {
15518                    context: Point::new(9, 0)..Point::new(10, 4),
15519                    primary: None,
15520                },
15521            ],
15522            cx,
15523        );
15524        multi_buffer.push_excerpts(
15525            buffer_2.clone(),
15526            [
15527                ExcerptRange {
15528                    context: Point::new(0, 0)..Point::new(3, 0),
15529                    primary: None,
15530                },
15531                ExcerptRange {
15532                    context: Point::new(5, 0)..Point::new(7, 0),
15533                    primary: None,
15534                },
15535                ExcerptRange {
15536                    context: Point::new(9, 0)..Point::new(10, 4),
15537                    primary: None,
15538                },
15539            ],
15540            cx,
15541        );
15542        multi_buffer.push_excerpts(
15543            buffer_3.clone(),
15544            [
15545                ExcerptRange {
15546                    context: Point::new(0, 0)..Point::new(3, 0),
15547                    primary: None,
15548                },
15549                ExcerptRange {
15550                    context: Point::new(5, 0)..Point::new(7, 0),
15551                    primary: None,
15552                },
15553                ExcerptRange {
15554                    context: Point::new(9, 0)..Point::new(10, 4),
15555                    primary: None,
15556                },
15557            ],
15558            cx,
15559        );
15560        multi_buffer
15561    });
15562    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15563        Editor::new(
15564            EditorMode::Full,
15565            multi_buffer,
15566            Some(project.clone()),
15567            true,
15568            window,
15569            cx,
15570        )
15571    });
15572
15573    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
15574    assert_eq!(
15575        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15576        full_text,
15577    );
15578
15579    multi_buffer_editor.update(cx, |editor, cx| {
15580        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15581    });
15582    assert_eq!(
15583        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15584        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15585        "After folding the first buffer, its text should not be displayed"
15586    );
15587
15588    multi_buffer_editor.update(cx, |editor, cx| {
15589        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15590    });
15591    assert_eq!(
15592        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15593        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15594        "After folding the second buffer, its text should not be displayed"
15595    );
15596
15597    multi_buffer_editor.update(cx, |editor, cx| {
15598        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15599    });
15600    assert_eq!(
15601        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15602        "\n\n\n\n\n",
15603        "After folding the third buffer, its text should not be displayed"
15604    );
15605
15606    // Emulate selection inside the fold logic, that should work
15607    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15608        editor
15609            .snapshot(window, cx)
15610            .next_line_boundary(Point::new(0, 4));
15611    });
15612
15613    multi_buffer_editor.update(cx, |editor, cx| {
15614        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15615    });
15616    assert_eq!(
15617        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15618        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15619        "After unfolding the second buffer, its text should be displayed"
15620    );
15621
15622    multi_buffer_editor.update(cx, |editor, cx| {
15623        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15624    });
15625    assert_eq!(
15626        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15627        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15628        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15629    );
15630
15631    multi_buffer_editor.update(cx, |editor, cx| {
15632        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15633    });
15634    assert_eq!(
15635        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15636        full_text,
15637        "After unfolding the all buffers, all original text should be displayed"
15638    );
15639}
15640
15641#[gpui::test]
15642async fn test_multi_buffer_single_excerpts_folding(cx: &mut TestAppContext) {
15643    init_test(cx, |_| {});
15644
15645    let sample_text_1 = "1111\n2222\n3333".to_string();
15646    let sample_text_2 = "4444\n5555\n6666".to_string();
15647    let sample_text_3 = "7777\n8888\n9999".to_string();
15648
15649    let fs = FakeFs::new(cx.executor());
15650    fs.insert_tree(
15651        path!("/a"),
15652        json!({
15653            "first.rs": sample_text_1,
15654            "second.rs": sample_text_2,
15655            "third.rs": sample_text_3,
15656        }),
15657    )
15658    .await;
15659    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15660    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15661    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15662    let worktree = project.update(cx, |project, cx| {
15663        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15664        assert_eq!(worktrees.len(), 1);
15665        worktrees.pop().unwrap()
15666    });
15667    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15668
15669    let buffer_1 = project
15670        .update(cx, |project, cx| {
15671            project.open_buffer((worktree_id, "first.rs"), cx)
15672        })
15673        .await
15674        .unwrap();
15675    let buffer_2 = project
15676        .update(cx, |project, cx| {
15677            project.open_buffer((worktree_id, "second.rs"), cx)
15678        })
15679        .await
15680        .unwrap();
15681    let buffer_3 = project
15682        .update(cx, |project, cx| {
15683            project.open_buffer((worktree_id, "third.rs"), cx)
15684        })
15685        .await
15686        .unwrap();
15687
15688    let multi_buffer = cx.new(|cx| {
15689        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15690        multi_buffer.push_excerpts(
15691            buffer_1.clone(),
15692            [ExcerptRange {
15693                context: Point::new(0, 0)..Point::new(3, 0),
15694                primary: None,
15695            }],
15696            cx,
15697        );
15698        multi_buffer.push_excerpts(
15699            buffer_2.clone(),
15700            [ExcerptRange {
15701                context: Point::new(0, 0)..Point::new(3, 0),
15702                primary: None,
15703            }],
15704            cx,
15705        );
15706        multi_buffer.push_excerpts(
15707            buffer_3.clone(),
15708            [ExcerptRange {
15709                context: Point::new(0, 0)..Point::new(3, 0),
15710                primary: None,
15711            }],
15712            cx,
15713        );
15714        multi_buffer
15715    });
15716
15717    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15718        Editor::new(
15719            EditorMode::Full,
15720            multi_buffer,
15721            Some(project.clone()),
15722            true,
15723            window,
15724            cx,
15725        )
15726    });
15727
15728    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15729    assert_eq!(
15730        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15731        full_text,
15732    );
15733
15734    multi_buffer_editor.update(cx, |editor, cx| {
15735        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15736    });
15737    assert_eq!(
15738        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15739        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15740        "After folding the first buffer, its text should not be displayed"
15741    );
15742
15743    multi_buffer_editor.update(cx, |editor, cx| {
15744        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15745    });
15746
15747    assert_eq!(
15748        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15749        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15750        "After folding the second buffer, its text should not be displayed"
15751    );
15752
15753    multi_buffer_editor.update(cx, |editor, cx| {
15754        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15755    });
15756    assert_eq!(
15757        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15758        "\n\n\n\n\n",
15759        "After folding the third buffer, its text should not be displayed"
15760    );
15761
15762    multi_buffer_editor.update(cx, |editor, cx| {
15763        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15764    });
15765    assert_eq!(
15766        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15767        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15768        "After unfolding the second buffer, its text should be displayed"
15769    );
15770
15771    multi_buffer_editor.update(cx, |editor, cx| {
15772        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15773    });
15774    assert_eq!(
15775        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15776        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15777        "After unfolding the first buffer, its text should be displayed"
15778    );
15779
15780    multi_buffer_editor.update(cx, |editor, cx| {
15781        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15782    });
15783    assert_eq!(
15784        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15785        full_text,
15786        "After unfolding all buffers, all original text should be displayed"
15787    );
15788}
15789
15790#[gpui::test]
15791async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut TestAppContext) {
15792    init_test(cx, |_| {});
15793
15794    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15795
15796    let fs = FakeFs::new(cx.executor());
15797    fs.insert_tree(
15798        path!("/a"),
15799        json!({
15800            "main.rs": sample_text,
15801        }),
15802    )
15803    .await;
15804    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15805    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15806    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15807    let worktree = project.update(cx, |project, cx| {
15808        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15809        assert_eq!(worktrees.len(), 1);
15810        worktrees.pop().unwrap()
15811    });
15812    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15813
15814    let buffer_1 = project
15815        .update(cx, |project, cx| {
15816            project.open_buffer((worktree_id, "main.rs"), cx)
15817        })
15818        .await
15819        .unwrap();
15820
15821    let multi_buffer = cx.new(|cx| {
15822        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15823        multi_buffer.push_excerpts(
15824            buffer_1.clone(),
15825            [ExcerptRange {
15826                context: Point::new(0, 0)
15827                    ..Point::new(
15828                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15829                        0,
15830                    ),
15831                primary: None,
15832            }],
15833            cx,
15834        );
15835        multi_buffer
15836    });
15837    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15838        Editor::new(
15839            EditorMode::Full,
15840            multi_buffer,
15841            Some(project.clone()),
15842            true,
15843            window,
15844            cx,
15845        )
15846    });
15847
15848    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15849    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15850        enum TestHighlight {}
15851        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15852        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15853        editor.highlight_text::<TestHighlight>(
15854            vec![highlight_range.clone()],
15855            HighlightStyle::color(Hsla::green()),
15856            cx,
15857        );
15858        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15859    });
15860
15861    let full_text = format!("\n\n\n{sample_text}\n");
15862    assert_eq!(
15863        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15864        full_text,
15865    );
15866}
15867
15868#[gpui::test]
15869async fn test_inline_completion_text(cx: &mut TestAppContext) {
15870    init_test(cx, |_| {});
15871
15872    // Simple insertion
15873    assert_highlighted_edits(
15874        "Hello, world!",
15875        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15876        true,
15877        cx,
15878        |highlighted_edits, cx| {
15879            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15880            assert_eq!(highlighted_edits.highlights.len(), 1);
15881            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15882            assert_eq!(
15883                highlighted_edits.highlights[0].1.background_color,
15884                Some(cx.theme().status().created_background)
15885            );
15886        },
15887    )
15888    .await;
15889
15890    // Replacement
15891    assert_highlighted_edits(
15892        "This is a test.",
15893        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15894        false,
15895        cx,
15896        |highlighted_edits, cx| {
15897            assert_eq!(highlighted_edits.text, "That is a test.");
15898            assert_eq!(highlighted_edits.highlights.len(), 1);
15899            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15900            assert_eq!(
15901                highlighted_edits.highlights[0].1.background_color,
15902                Some(cx.theme().status().created_background)
15903            );
15904        },
15905    )
15906    .await;
15907
15908    // Multiple edits
15909    assert_highlighted_edits(
15910        "Hello, world!",
15911        vec![
15912            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15913            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15914        ],
15915        false,
15916        cx,
15917        |highlighted_edits, cx| {
15918            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15919            assert_eq!(highlighted_edits.highlights.len(), 2);
15920            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15921            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15922            assert_eq!(
15923                highlighted_edits.highlights[0].1.background_color,
15924                Some(cx.theme().status().created_background)
15925            );
15926            assert_eq!(
15927                highlighted_edits.highlights[1].1.background_color,
15928                Some(cx.theme().status().created_background)
15929            );
15930        },
15931    )
15932    .await;
15933
15934    // Multiple lines with edits
15935    assert_highlighted_edits(
15936        "First line\nSecond line\nThird line\nFourth line",
15937        vec![
15938            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15939            (
15940                Point::new(2, 0)..Point::new(2, 10),
15941                "New third line".to_string(),
15942            ),
15943            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15944        ],
15945        false,
15946        cx,
15947        |highlighted_edits, cx| {
15948            assert_eq!(
15949                highlighted_edits.text,
15950                "Second modified\nNew third line\nFourth updated line"
15951            );
15952            assert_eq!(highlighted_edits.highlights.len(), 3);
15953            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15954            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15955            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15956            for highlight in &highlighted_edits.highlights {
15957                assert_eq!(
15958                    highlight.1.background_color,
15959                    Some(cx.theme().status().created_background)
15960                );
15961            }
15962        },
15963    )
15964    .await;
15965}
15966
15967#[gpui::test]
15968async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15969    init_test(cx, |_| {});
15970
15971    // Deletion
15972    assert_highlighted_edits(
15973        "Hello, world!",
15974        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15975        true,
15976        cx,
15977        |highlighted_edits, cx| {
15978            assert_eq!(highlighted_edits.text, "Hello, world!");
15979            assert_eq!(highlighted_edits.highlights.len(), 1);
15980            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15981            assert_eq!(
15982                highlighted_edits.highlights[0].1.background_color,
15983                Some(cx.theme().status().deleted_background)
15984            );
15985        },
15986    )
15987    .await;
15988
15989    // Insertion
15990    assert_highlighted_edits(
15991        "Hello, world!",
15992        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15993        true,
15994        cx,
15995        |highlighted_edits, cx| {
15996            assert_eq!(highlighted_edits.highlights.len(), 1);
15997            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15998            assert_eq!(
15999                highlighted_edits.highlights[0].1.background_color,
16000                Some(cx.theme().status().created_background)
16001            );
16002        },
16003    )
16004    .await;
16005}
16006
16007async fn assert_highlighted_edits(
16008    text: &str,
16009    edits: Vec<(Range<Point>, String)>,
16010    include_deletions: bool,
16011    cx: &mut TestAppContext,
16012    assertion_fn: impl Fn(HighlightedText, &App),
16013) {
16014    let window = cx.add_window(|window, cx| {
16015        let buffer = MultiBuffer::build_simple(text, cx);
16016        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16017    });
16018    let cx = &mut VisualTestContext::from_window(*window, cx);
16019
16020    let (buffer, snapshot) = window
16021        .update(cx, |editor, _window, cx| {
16022            (
16023                editor.buffer().clone(),
16024                editor.buffer().read(cx).snapshot(cx),
16025            )
16026        })
16027        .unwrap();
16028
16029    let edits = edits
16030        .into_iter()
16031        .map(|(range, edit)| {
16032            (
16033                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16034                edit,
16035            )
16036        })
16037        .collect::<Vec<_>>();
16038
16039    let text_anchor_edits = edits
16040        .clone()
16041        .into_iter()
16042        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16043        .collect::<Vec<_>>();
16044
16045    let edit_preview = window
16046        .update(cx, |_, _window, cx| {
16047            buffer
16048                .read(cx)
16049                .as_singleton()
16050                .unwrap()
16051                .read(cx)
16052                .preview_edits(text_anchor_edits.into(), cx)
16053        })
16054        .unwrap()
16055        .await;
16056
16057    cx.update(|_window, cx| {
16058        let highlighted_edits = inline_completion_edit_text(
16059            &snapshot.as_singleton().unwrap().2,
16060            &edits,
16061            &edit_preview,
16062            include_deletions,
16063            cx,
16064        );
16065        assertion_fn(highlighted_edits, cx)
16066    });
16067}
16068
16069#[gpui::test]
16070async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16071    init_test(cx, |_| {});
16072    let capabilities = lsp::ServerCapabilities {
16073        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16074            prepare_provider: Some(true),
16075            work_done_progress_options: Default::default(),
16076        })),
16077        ..Default::default()
16078    };
16079    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16080
16081    cx.set_state(indoc! {"
16082        struct Fˇoo {}
16083    "});
16084
16085    cx.update_editor(|editor, _, cx| {
16086        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16087        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16088        editor.highlight_background::<DocumentHighlightRead>(
16089            &[highlight_range],
16090            |c| c.editor_document_highlight_read_background,
16091            cx,
16092        );
16093    });
16094
16095    let mut prepare_rename_handler =
16096        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16097            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16098                start: lsp::Position {
16099                    line: 0,
16100                    character: 7,
16101                },
16102                end: lsp::Position {
16103                    line: 0,
16104                    character: 10,
16105                },
16106            })))
16107        });
16108    let prepare_rename_task = cx
16109        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16110        .expect("Prepare rename was not started");
16111    prepare_rename_handler.next().await.unwrap();
16112    prepare_rename_task.await.expect("Prepare rename failed");
16113
16114    let mut rename_handler =
16115        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16116            let edit = lsp::TextEdit {
16117                range: lsp::Range {
16118                    start: lsp::Position {
16119                        line: 0,
16120                        character: 7,
16121                    },
16122                    end: lsp::Position {
16123                        line: 0,
16124                        character: 10,
16125                    },
16126                },
16127                new_text: "FooRenamed".to_string(),
16128            };
16129            Ok(Some(lsp::WorkspaceEdit::new(
16130                // Specify the same edit twice
16131                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16132            )))
16133        });
16134    let rename_task = cx
16135        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16136        .expect("Confirm rename was not started");
16137    rename_handler.next().await.unwrap();
16138    rename_task.await.expect("Confirm rename failed");
16139    cx.run_until_parked();
16140
16141    // Despite two edits, only one is actually applied as those are identical
16142    cx.assert_editor_state(indoc! {"
16143        struct FooRenamedˇ {}
16144    "});
16145}
16146
16147#[gpui::test]
16148async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16149    init_test(cx, |_| {});
16150    // These capabilities indicate that the server does not support prepare rename.
16151    let capabilities = lsp::ServerCapabilities {
16152        rename_provider: Some(lsp::OneOf::Left(true)),
16153        ..Default::default()
16154    };
16155    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16156
16157    cx.set_state(indoc! {"
16158        struct Fˇoo {}
16159    "});
16160
16161    cx.update_editor(|editor, _window, cx| {
16162        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16163        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16164        editor.highlight_background::<DocumentHighlightRead>(
16165            &[highlight_range],
16166            |c| c.editor_document_highlight_read_background,
16167            cx,
16168        );
16169    });
16170
16171    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16172        .expect("Prepare rename was not started")
16173        .await
16174        .expect("Prepare rename failed");
16175
16176    let mut rename_handler =
16177        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16178            let edit = lsp::TextEdit {
16179                range: lsp::Range {
16180                    start: lsp::Position {
16181                        line: 0,
16182                        character: 7,
16183                    },
16184                    end: lsp::Position {
16185                        line: 0,
16186                        character: 10,
16187                    },
16188                },
16189                new_text: "FooRenamed".to_string(),
16190            };
16191            Ok(Some(lsp::WorkspaceEdit::new(
16192                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16193            )))
16194        });
16195    let rename_task = cx
16196        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16197        .expect("Confirm rename was not started");
16198    rename_handler.next().await.unwrap();
16199    rename_task.await.expect("Confirm rename failed");
16200    cx.run_until_parked();
16201
16202    // Correct range is renamed, as `surrounding_word` is used to find it.
16203    cx.assert_editor_state(indoc! {"
16204        struct FooRenamedˇ {}
16205    "});
16206}
16207
16208#[gpui::test]
16209async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16210    init_test(cx, |_| {});
16211    let mut cx = EditorTestContext::new(cx).await;
16212
16213    let language = Arc::new(
16214        Language::new(
16215            LanguageConfig::default(),
16216            Some(tree_sitter_html::LANGUAGE.into()),
16217        )
16218        .with_brackets_query(
16219            r#"
16220            ("<" @open "/>" @close)
16221            ("</" @open ">" @close)
16222            ("<" @open ">" @close)
16223            ("\"" @open "\"" @close)
16224            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16225        "#,
16226        )
16227        .unwrap(),
16228    );
16229    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16230
16231    cx.set_state(indoc! {"
16232        <span>ˇ</span>
16233    "});
16234    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16235    cx.assert_editor_state(indoc! {"
16236        <span>
16237        ˇ
16238        </span>
16239    "});
16240
16241    cx.set_state(indoc! {"
16242        <span><span></span>ˇ</span>
16243    "});
16244    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16245    cx.assert_editor_state(indoc! {"
16246        <span><span></span>
16247        ˇ</span>
16248    "});
16249
16250    cx.set_state(indoc! {"
16251        <span>ˇ
16252        </span>
16253    "});
16254    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16255    cx.assert_editor_state(indoc! {"
16256        <span>
16257        ˇ
16258        </span>
16259    "});
16260}
16261
16262fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16263    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16264    point..point
16265}
16266
16267fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16268    let (text, ranges) = marked_text_ranges(marked_text, true);
16269    assert_eq!(editor.text(cx), text);
16270    assert_eq!(
16271        editor.selections.ranges(cx),
16272        ranges,
16273        "Assert selections are {}",
16274        marked_text
16275    );
16276}
16277
16278pub fn handle_signature_help_request(
16279    cx: &mut EditorLspTestContext,
16280    mocked_response: lsp::SignatureHelp,
16281) -> impl Future<Output = ()> {
16282    let mut request =
16283        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16284            let mocked_response = mocked_response.clone();
16285            async move { Ok(Some(mocked_response)) }
16286        });
16287
16288    async move {
16289        request.next().await;
16290    }
16291}
16292
16293/// Handle completion request passing a marked string specifying where the completion
16294/// should be triggered from using '|' character, what range should be replaced, and what completions
16295/// should be returned using '<' and '>' to delimit the range
16296pub fn handle_completion_request(
16297    cx: &mut EditorLspTestContext,
16298    marked_string: &str,
16299    completions: Vec<&'static str>,
16300    counter: Arc<AtomicUsize>,
16301) -> impl Future<Output = ()> {
16302    let complete_from_marker: TextRangeMarker = '|'.into();
16303    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16304    let (_, mut marked_ranges) = marked_text_ranges_by(
16305        marked_string,
16306        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16307    );
16308
16309    let complete_from_position =
16310        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16311    let replace_range =
16312        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16313
16314    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16315        let completions = completions.clone();
16316        counter.fetch_add(1, atomic::Ordering::Release);
16317        async move {
16318            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16319            assert_eq!(
16320                params.text_document_position.position,
16321                complete_from_position
16322            );
16323            Ok(Some(lsp::CompletionResponse::Array(
16324                completions
16325                    .iter()
16326                    .map(|completion_text| lsp::CompletionItem {
16327                        label: completion_text.to_string(),
16328                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16329                            range: replace_range,
16330                            new_text: completion_text.to_string(),
16331                        })),
16332                        ..Default::default()
16333                    })
16334                    .collect(),
16335            )))
16336        }
16337    });
16338
16339    async move {
16340        request.next().await;
16341    }
16342}
16343
16344fn handle_resolve_completion_request(
16345    cx: &mut EditorLspTestContext,
16346    edits: Option<Vec<(&'static str, &'static str)>>,
16347) -> impl Future<Output = ()> {
16348    let edits = edits.map(|edits| {
16349        edits
16350            .iter()
16351            .map(|(marked_string, new_text)| {
16352                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16353                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16354                lsp::TextEdit::new(replace_range, new_text.to_string())
16355            })
16356            .collect::<Vec<_>>()
16357    });
16358
16359    let mut request =
16360        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16361            let edits = edits.clone();
16362            async move {
16363                Ok(lsp::CompletionItem {
16364                    additional_text_edits: edits,
16365                    ..Default::default()
16366                })
16367            }
16368        });
16369
16370    async move {
16371        request.next().await;
16372    }
16373}
16374
16375pub(crate) fn update_test_language_settings(
16376    cx: &mut TestAppContext,
16377    f: impl Fn(&mut AllLanguageSettingsContent),
16378) {
16379    cx.update(|cx| {
16380        SettingsStore::update_global(cx, |store, cx| {
16381            store.update_user_settings::<AllLanguageSettings>(cx, f);
16382        });
16383    });
16384}
16385
16386pub(crate) fn update_test_project_settings(
16387    cx: &mut TestAppContext,
16388    f: impl Fn(&mut ProjectSettings),
16389) {
16390    cx.update(|cx| {
16391        SettingsStore::update_global(cx, |store, cx| {
16392            store.update_user_settings::<ProjectSettings>(cx, f);
16393        });
16394    });
16395}
16396
16397pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16398    cx.update(|cx| {
16399        assets::Assets.load_test_fonts(cx);
16400        let store = SettingsStore::test(cx);
16401        cx.set_global(store);
16402        theme::init(theme::LoadThemes::JustBase, cx);
16403        release_channel::init(SemanticVersion::default(), cx);
16404        client::init_settings(cx);
16405        language::init(cx);
16406        Project::init_settings(cx);
16407        workspace::init_settings(cx);
16408        crate::init(cx);
16409    });
16410
16411    update_test_language_settings(cx, f);
16412}
16413
16414#[track_caller]
16415fn assert_hunk_revert(
16416    not_reverted_text_with_selections: &str,
16417    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16418    expected_reverted_text_with_selections: &str,
16419    base_text: &str,
16420    cx: &mut EditorLspTestContext,
16421) {
16422    cx.set_state(not_reverted_text_with_selections);
16423    cx.set_head_text(base_text);
16424    cx.executor().run_until_parked();
16425
16426    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16427        let snapshot = editor.snapshot(window, cx);
16428        let reverted_hunk_statuses = snapshot
16429            .buffer_snapshot
16430            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16431            .map(|hunk| hunk.status())
16432            .collect::<Vec<_>>();
16433
16434        editor.git_restore(&Default::default(), window, cx);
16435        reverted_hunk_statuses
16436    });
16437    cx.executor().run_until_parked();
16438    cx.assert_editor_state(expected_reverted_text_with_selections);
16439    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16440}